From: Sasha Levin Date: Mon, 17 Aug 2026 03:25:46 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;ds=inline;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/fscrypt-replace-mk_users-keyring-with-simple-list.patch b/queue-5.10/fscrypt-replace-mk_users-keyring-with-simple-list.patch new file mode 100644 index 0000000000..d98a1fbdfb --- /dev/null +++ b/queue-5.10/fscrypt-replace-mk_users-keyring-with-simple-list.patch @@ -0,0 +1,448 @@ +From f4f836c1c2185ccffa01006331ec19295775e80b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 11:31:41 -0700 +Subject: fscrypt: Replace mk_users keyring with simple list + +From: Eric Biggers + +commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. + +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 | 212 ++++++++++++++++-------------------- + 2 files changed, 113 insertions(+), 131 deletions(-) + +diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h +index b746d7df37582..72db3df9dfad7 100644 +--- a/fs/crypto/fscrypt_private.h ++++ b/fs/crypto/fscrypt_private.h +@@ -403,6 +403,19 @@ fscrypt_is_key_prepared(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 + */ +@@ -489,19 +502,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 ->mk_secret.) ++ * 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 02f8bf8bd54da..51cd45785f0c9 100644 +--- a/fs/crypto/keyring.c ++++ b/fs/crypto/keyring.c +@@ -64,18 +64,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(refcount_read(&mk->mk_active_refs) != 0); +- key_put(mk->mk_users); +- mk->mk_users = NULL; ++ clear_mk_users(mk); + call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); + } + +@@ -141,8 +142,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 +@@ -156,30 +157,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) + { +@@ -318,91 +298,94 @@ fscrypt_find_master_key(struct super_block *sb, + return mk; + } + +-static int allocate_master_key_users_keyring(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_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; +-} ++ struct fscrypt_master_key_user *mk_user; ++ kuid_t uid = current_fsuid(); + +-/* +- * 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) +-{ +- char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; +- key_ref_t keyref; +- +- 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(sizeof(*mk_user), GFP_KERNEL); ++ 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); + } + + /* +@@ -426,13 +409,12 @@ 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); + + 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; +@@ -460,19 +442,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; +@@ -787,7 +763,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; +@@ -799,13 +774,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: +@@ -998,16 +970,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 +@@ -1095,6 +1069,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))) +@@ -1127,19 +1103,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: +-- +2.53.0 + diff --git a/queue-5.10/mips-sched-fix-cpumask_offstack-memory-corruption.patch b/queue-5.10/mips-sched-fix-cpumask_offstack-memory-corruption.patch new file mode 100644 index 0000000000..d61d2f192a --- /dev/null +++ b/queue-5.10/mips-sched-fix-cpumask_offstack-memory-corruption.patch @@ -0,0 +1,99 @@ +From 36481f7263218b5ab8ebf66cbe88df52e981fd14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 20:36:33 -0400 +Subject: mips: sched: Fix CPUMASK_OFFSTACK memory corruption + +From: Aaron Tomlin + +[ Upstream commit 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 ] + +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: Sasha Levin +--- + 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 6c590ef276482..5feffc8708ada 100644 +--- a/arch/mips/kernel/mips-mt-fpaff.c ++++ b/arch/mips/kernel/mips-mt-fpaff.c +@@ -70,11 +70,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; ++ } + + get_online_cpus(); + rcu_read_lock(); +@@ -83,7 +88,8 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, + if (!p) { + rcu_read_unlock(); + put_online_cpus(); +- return -ESRCH; ++ retval = -ESRCH; ++ goto out_free_new_mask; + } + + /* Prevent p going away */ +@@ -94,13 +100,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; +@@ -141,13 +143,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); + put_online_cpus(); ++out_free_new_mask: ++ free_cpumask_var(new_mask); + return retval; + } + +-- +2.53.0 + diff --git a/queue-5.10/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch b/queue-5.10/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch new file mode 100644 index 0000000000..d92363e0ff --- /dev/null +++ b/queue-5.10/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch @@ -0,0 +1,166 @@ +From d2065fbd2e7334b6c9d18fa13d2fd2ff512748ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Aug 2026 13:23:59 -0700 +Subject: 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 + +From: Zhu Yanjun + +[ 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 +(cherry picked from commit 35744ab3d03c5fca8c1752f53fc8fc674e14c561) +[Harshit: Minor conflict resolution pr_err() vs rxe_dbg_dev() usage] +Signed-off-by: Harshit Mogalapalli +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 035f226af1336..edd9345da295c 100644 +--- a/drivers/infiniband/sw/rxe/rxe_mmap.c ++++ b/drivers/infiniband/sw/rxe/rxe_mmap.c +@@ -94,18 +94,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); + pr_err("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; + } +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index 50857fa086..181ae305b5 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -351,3 +351,6 @@ bluetooth-l2cap-fix-uaf-in-channel-timeout-by-holdin.patch bluetooth-6lowpan-fix-using-chan-conn-as-indication-.patch ima-fix-out-of-bounds-read-in-xattr_verify.patch futex-prevent-robust-futex-exit-race-some-more.patch +rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch +mips-sched-fix-cpumask_offstack-memory-corruption.patch +fscrypt-replace-mk_users-keyring-with-simple-list.patch diff --git a/queue-5.15/fscrypt-replace-mk_users-keyring-with-simple-list.patch b/queue-5.15/fscrypt-replace-mk_users-keyring-with-simple-list.patch new file mode 100644 index 0000000000..8ee71a4914 --- /dev/null +++ b/queue-5.15/fscrypt-replace-mk_users-keyring-with-simple-list.patch @@ -0,0 +1,448 @@ +From 61d4250cea229fc032210fc00b1252bba6d0e98e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 11:31:26 -0700 +Subject: fscrypt: Replace mk_users keyring with simple list + +From: Eric Biggers + +commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. + +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 | 212 ++++++++++++++++-------------------- + 2 files changed, 113 insertions(+), 131 deletions(-) + +diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h +index 373c434b375c0..555d942342ccc 100644 +--- a/fs/crypto/fscrypt_private.h ++++ b/fs/crypto/fscrypt_private.h +@@ -402,6 +402,19 @@ fscrypt_is_key_prepared(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 + */ +@@ -488,19 +501,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 ->mk_secret.) ++ * 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 02f8bf8bd54da..51cd45785f0c9 100644 +--- a/fs/crypto/keyring.c ++++ b/fs/crypto/keyring.c +@@ -64,18 +64,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(refcount_read(&mk->mk_active_refs) != 0); +- key_put(mk->mk_users); +- mk->mk_users = NULL; ++ clear_mk_users(mk); + call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); + } + +@@ -141,8 +142,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 +@@ -156,30 +157,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) + { +@@ -318,91 +298,94 @@ fscrypt_find_master_key(struct super_block *sb, + return mk; + } + +-static int allocate_master_key_users_keyring(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_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; +-} ++ struct fscrypt_master_key_user *mk_user; ++ kuid_t uid = current_fsuid(); + +-/* +- * 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) +-{ +- char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; +- key_ref_t keyref; +- +- 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(sizeof(*mk_user), GFP_KERNEL); ++ 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); + } + + /* +@@ -426,13 +409,12 @@ 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); + + 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; +@@ -460,19 +442,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; +@@ -787,7 +763,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; +@@ -799,13 +774,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: +@@ -998,16 +970,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 +@@ -1095,6 +1069,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))) +@@ -1127,19 +1103,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: +-- +2.53.0 + diff --git a/queue-5.15/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch b/queue-5.15/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch new file mode 100644 index 0000000000..fb08657419 --- /dev/null +++ b/queue-5.15/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch @@ -0,0 +1,166 @@ +From 44d7d080a04bfcc2df9e77ab36fb59c0645bfd9b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Aug 2026 13:23:59 -0700 +Subject: 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 + +From: Zhu Yanjun + +[ 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 +(cherry picked from commit 35744ab3d03c5fca8c1752f53fc8fc674e14c561) +[Harshit: Minor conflict resolution pr_err() vs rxe_dbg_dev() usage] +Signed-off-by: Harshit Mogalapalli +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 035f226af1336..edd9345da295c 100644 +--- a/drivers/infiniband/sw/rxe/rxe_mmap.c ++++ b/drivers/infiniband/sw/rxe/rxe_mmap.c +@@ -94,18 +94,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); + pr_err("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; + } +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 1783532e36..f66a48316d 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -411,3 +411,5 @@ bluetooth-l2cap-fix-uaf-in-channel-timeout-by-holdin.patch bluetooth-6lowpan-fix-using-chan-conn-as-indication-.patch futex-prevent-robust-futex-exit-race-some-more.patch pinctrl-renesas-rzg2l-use-enotsupp-instead-of-eopnot.patch +rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch +fscrypt-replace-mk_users-keyring-with-simple-list.patch diff --git a/queue-6.1/fscrypt-replace-mk_users-keyring-with-simple-list.patch b/queue-6.1/fscrypt-replace-mk_users-keyring-with-simple-list.patch new file mode 100644 index 0000000000..798ee93612 --- /dev/null +++ b/queue-6.1/fscrypt-replace-mk_users-keyring-with-simple-list.patch @@ -0,0 +1,448 @@ +From 9c99f9f90ca8f04f0a4967fa642ac57e7eed3c59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 11:30:57 -0700 +Subject: fscrypt: Replace mk_users keyring with simple list + +From: Eric Biggers + +commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. + +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 | 212 ++++++++++++++++-------------------- + 2 files changed, 113 insertions(+), 131 deletions(-) + +diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h +index 88414cbd97aee..aabc3a4751df7 100644 +--- a/fs/crypto/fscrypt_private.h ++++ b/fs/crypto/fscrypt_private.h +@@ -423,6 +423,19 @@ fscrypt_is_key_prepared(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 + */ +@@ -513,19 +526,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 ->mk_secret.) ++ * 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 2a24b1f0ae688..e1cce06f43439 100644 +--- a/fs/crypto/keyring.c ++++ b/fs/crypto/keyring.c +@@ -64,18 +64,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(refcount_read(&mk->mk_active_refs) != 0); +- key_put(mk->mk_users); +- mk->mk_users = NULL; ++ clear_mk_users(mk); + call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); + } + +@@ -144,8 +145,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 +@@ -159,30 +160,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) + { +@@ -321,91 +301,94 @@ fscrypt_find_master_key(struct super_block *sb, + return mk; + } + +-static int allocate_master_key_users_keyring(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_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; +-} ++ struct fscrypt_master_key_user *mk_user; ++ kuid_t uid = current_fsuid(); + +-/* +- * 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) +-{ +- char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; +- key_ref_t keyref; +- +- 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(sizeof(*mk_user), GFP_KERNEL); ++ 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); + } + + /* +@@ -429,13 +412,12 @@ 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); + + 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; +@@ -463,19 +445,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; +@@ -830,7 +806,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; +@@ -842,13 +817,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: +@@ -1041,16 +1013,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 +@@ -1138,6 +1112,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))) +@@ -1170,19 +1146,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: +-- +2.53.0 + diff --git a/queue-6.1/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch b/queue-6.1/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch new file mode 100644 index 0000000000..b329deb9a1 --- /dev/null +++ b/queue-6.1/rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch @@ -0,0 +1,166 @@ +From 6d97b8d82a84c782b3e17770ce2f8e006b31c9e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Aug 2026 13:23:59 -0700 +Subject: 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 + +From: Zhu Yanjun + +[ 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 +(cherry picked from commit 35744ab3d03c5fca8c1752f53fc8fc674e14c561) +[Harshit: Minor conflict resolution pr_err() vs rxe_dbg_dev() usage] +Signed-off-by: Harshit Mogalapalli +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 9149b60954296..2749ed48b014a 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); + pr_err("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; + } +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-adapt-sockmap-update-error-handling.patch b/queue-6.1/selftests-bpf-adapt-sockmap-update-error-handling.patch new file mode 100644 index 0000000000..c3f7ebff7e --- /dev/null +++ b/queue-6.1/selftests-bpf-adapt-sockmap-update-error-handling.patch @@ -0,0 +1,92 @@ +From 676d49ec011c70b9563b0054f0aa2db2747761ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:58 +0200 +Subject: selftests/bpf: Adapt sockmap update error handling + +From: Michal Luczaj + +[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ] + +Update sockmap_listen to accommodate the recent change in sockmap that +rejects unbound UDP sockets. + +TCP: Reject unbound and bound (unless established or listening). +UDP: Accept only bound sockets. + +While at it, migrate to ASSERT_* and enforce reverse xmas tree. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +index cef5d35951711..2112b01a4b03a 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +@@ -341,8 +341,8 @@ static void test_insert_invalid(int family, int sotype, int mapfd) + static void test_insert_opened(int family, int sotype, int mapfd) + { + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + s = xsocket(family, sotype, 0); + if (s == -1) +@@ -351,11 +351,8 @@ static void test_insert_opened(int family, int sotype, int mapfd) + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (sotype == SOCK_STREAM) { +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); +- } else if (err) +- FAIL_ERRNO("map_update: expected success"); ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + xclose(s); + } + +@@ -364,8 +361,8 @@ static void test_insert_bound(int family, int sotype, int mapfd) + struct sockaddr_storage addr; + socklen_t len; + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + init_addr_loopback(family, &addr, &len); + +@@ -380,8 +377,12 @@ static void test_insert_bound(int family, int sotype, int mapfd) + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); ++ if (sotype == SOCK_STREAM) { ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); ++ } else { ++ ASSERT_OK(err, "map_update"); ++ } + close: + xclose(s); + } +@@ -1480,7 +1481,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, + /* insert */ + TEST(test_insert_invalid), + TEST(test_insert_opened), +- TEST(test_insert_bound, SOCK_STREAM), ++ TEST(test_insert_bound), + TEST(test_insert), + /* delete */ + TEST(test_delete_after_insert), +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-fail-unbound-udp-on-sockmap-update.patch b/queue-6.1/selftests-bpf-fail-unbound-udp-on-sockmap-update.patch new file mode 100644 index 0000000000..59814b171c --- /dev/null +++ b/queue-6.1/selftests-bpf-fail-unbound-udp-on-sockmap-update.patch @@ -0,0 +1,55 @@ +From f792f83a4123bfa0cbc617b53ac85f5ee56f64e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:59 +0200 +Subject: selftests/bpf: Fail unbound UDP on sockmap update + +From: Michal Luczaj + +[ Upstream commit 203b06932777b9ad5085319389dea566f5c2ca63 ] + +sockmap now rejects unbound UDP sockets. Adjust test_maps. While at it, +check socket()'s return value. + +This effectively reverts commit c39aa2159974 ("bpf, selftests: Fix +test_maps now that sockmap supports UDP"). + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-4-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/test_maps.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c +index 81cd48cc80c23..73878ec8ec7d0 100644 +--- a/tools/testing/selftests/bpf/test_maps.c ++++ b/tools/testing/selftests/bpf/test_maps.c +@@ -752,16 +752,15 @@ static void test_sockmap(unsigned int tasks, void *data) + goto out_sockmap; + } + +- /* Test update with unsupported UDP socket */ ++ /* Test update with unsupported unbound UDP socket */ + udp = socket(AF_INET, SOCK_DGRAM, 0); +- i = 0; +- err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY); +- if (err) { +- printf("Failed socket update SOCK_DGRAM '%i:%i'\n", +- i, udp); ++ CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno); ++ err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY); ++ close(udp); ++ if (!err) { ++ printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp); + goto out_sockmap; + } +- close(udp); + + /* Test update without programs */ + for (i = 0; i < 6; i++) { +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 5540a5059a..964263ce86 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -550,3 +550,7 @@ futex-prevent-robust-futex-exit-race-some-more.patch fortify-refactor-test_fortify-makefile-to-fix-some-b.patch fortify-disable-wstringop-overread-in-tests.patch pinctrl-renesas-rzg2l-use-enotsupp-instead-of-eopnot.patch +rdma-rxe-fix-a-use-after-free-problem-in-rxe_mmap.patch +fscrypt-replace-mk_users-keyring-with-simple-list.patch +selftests-bpf-adapt-sockmap-update-error-handling.patch +selftests-bpf-fail-unbound-udp-on-sockmap-update.patch diff --git a/queue-6.12/selftests-bpf-adapt-sockmap-update-error-handling.patch b/queue-6.12/selftests-bpf-adapt-sockmap-update-error-handling.patch new file mode 100644 index 0000000000..134576905e --- /dev/null +++ b/queue-6.12/selftests-bpf-adapt-sockmap-update-error-handling.patch @@ -0,0 +1,92 @@ +From de5a0c4a4ae0202b97eba7018dba6fbb67a21b54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:58 +0200 +Subject: selftests/bpf: Adapt sockmap update error handling + +From: Michal Luczaj + +[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ] + +Update sockmap_listen to accommodate the recent change in sockmap that +rejects unbound UDP sockets. + +TCP: Reject unbound and bound (unless established or listening). +UDP: Accept only bound sockets. + +While at it, migrate to ASSERT_* and enforce reverse xmas tree. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +index 1cfed83156b03..b04b8188b1514 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + int family, int sotype, int mapfd) + { + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + s = xsocket(family, sotype, 0); + if (s == -1) +@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (sotype == SOCK_STREAM) { +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); +- } else if (err) +- FAIL_ERRNO("map_update: expected success"); ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + xclose(s); + } + +@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + struct sockaddr_storage addr; + socklen_t len = 0; + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + init_addr_loopback(family, &addr, &len); + +@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); ++ if (sotype == SOCK_STREAM) { ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); ++ } else { ++ ASSERT_OK(err, "map_update"); ++ } + close: + xclose(s); + } +@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, + /* insert */ + TEST(test_insert_invalid), + TEST(test_insert_opened), +- TEST(test_insert_bound, SOCK_STREAM), ++ TEST(test_insert_bound), + TEST(test_insert), + /* delete */ + TEST(test_delete_after_insert), +-- +2.53.0 + diff --git a/queue-6.12/selftests-bpf-ensure-udp-sockets-are-bound.patch b/queue-6.12/selftests-bpf-ensure-udp-sockets-are-bound.patch new file mode 100644 index 0000000000..d419a6d465 --- /dev/null +++ b/queue-6.12/selftests-bpf-ensure-udp-sockets-are-bound.patch @@ -0,0 +1,56 @@ +From 8cf293a8d6d96dd7fc12163be0dbe030c70bc9b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:56 +0200 +Subject: selftests/bpf: Ensure UDP sockets are bound + +From: Michal Luczaj + +[ Upstream commit fe3ff273767ef22fe8a7cb3816f264927c190e50 ] + +Update sockmap_basic tests to bind sockets before they are used. This +accommodates the recent change in sockmap that rejects unbound UDP sockets. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-1-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +index fb08c565d6aad..99522cf68dc8c 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +@@ -703,7 +703,7 @@ static void test_sockmap_many_socket(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -772,7 +772,7 @@ static void test_sockmap_many_maps(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -843,7 +843,7 @@ static void test_sockmap_same_sock(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 4606336adb..ec94fdb6ae 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -99,3 +99,5 @@ futex-prevent-robust-futex-exit-race-some-more.patch kunit-fortify-replace-volatile-with-optimizer_hide_v.patch kunit-fortify-add-back-volatile-for-sizeof-constants.patch pinctrl-renesas-rzg2l-use-enotsupp-instead-of-eopnot.patch +selftests-bpf-ensure-udp-sockets-are-bound.patch +selftests-bpf-adapt-sockmap-update-error-handling.patch diff --git a/queue-6.18/selftests-bpf-adapt-sockmap-update-error-handling.patch b/queue-6.18/selftests-bpf-adapt-sockmap-update-error-handling.patch new file mode 100644 index 0000000000..a9f6454ae8 --- /dev/null +++ b/queue-6.18/selftests-bpf-adapt-sockmap-update-error-handling.patch @@ -0,0 +1,92 @@ +From 2c63bc8380241946ff7004e8471ff29c29601cbe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:58 +0200 +Subject: selftests/bpf: Adapt sockmap update error handling + +From: Michal Luczaj + +[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ] + +Update sockmap_listen to accommodate the recent change in sockmap that +rejects unbound UDP sockets. + +TCP: Reject unbound and bound (unless established or listening). +UDP: Accept only bound sockets. + +While at it, migrate to ASSERT_* and enforce reverse xmas tree. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +index f1bdccc7e4e79..0a744f786e107 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + int family, int sotype, int mapfd) + { + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + s = xsocket(family, sotype, 0); + if (s == -1) +@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (sotype == SOCK_STREAM) { +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); +- } else if (err) +- FAIL_ERRNO("map_update: expected success"); ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + xclose(s); + } + +@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + struct sockaddr_storage addr; + socklen_t len = 0; + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + init_addr_loopback(family, &addr, &len); + +@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); ++ if (sotype == SOCK_STREAM) { ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); ++ } else { ++ ASSERT_OK(err, "map_update"); ++ } + close: + xclose(s); + } +@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, + /* insert */ + TEST(test_insert_invalid), + TEST(test_insert_opened), +- TEST(test_insert_bound, SOCK_STREAM), ++ TEST(test_insert_bound), + TEST(test_insert), + /* delete */ + TEST(test_delete_after_insert), +-- +2.53.0 + diff --git a/queue-6.18/selftests-bpf-ensure-udp-sockets-are-bound.patch b/queue-6.18/selftests-bpf-ensure-udp-sockets-are-bound.patch new file mode 100644 index 0000000000..cba7ae6333 --- /dev/null +++ b/queue-6.18/selftests-bpf-ensure-udp-sockets-are-bound.patch @@ -0,0 +1,56 @@ +From 55a1723b5c60cccdd2b3c8de351744d761684970 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:56 +0200 +Subject: selftests/bpf: Ensure UDP sockets are bound + +From: Michal Luczaj + +[ Upstream commit fe3ff273767ef22fe8a7cb3816f264927c190e50 ] + +Update sockmap_basic tests to bind sockets before they are used. This +accommodates the recent change in sockmap that rejects unbound UDP sockets. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-1-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +index 1e3e4392dcca0..54fd84203dc2b 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +@@ -786,7 +786,7 @@ static void test_sockmap_many_socket(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -855,7 +855,7 @@ static void test_sockmap_many_maps(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -926,7 +926,7 @@ static void test_sockmap_same_sock(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index c47cecae67..0b49004922 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -135,3 +135,5 @@ futex-prevent-robust-futex-exit-race-some-more.patch netfilter-nf_tables-avoid-softlockup-warnings-in-nft.patch bluetooth-btrtl-fix-rtl8761b-bu-broken-le-extended-s.patch bluetooth-btusb-add-tp-link-ub600-for-realtek-8761bu.patch +selftests-bpf-ensure-udp-sockets-are-bound.patch +selftests-bpf-adapt-sockmap-update-error-handling.patch diff --git a/queue-6.6/fscrypt-replace-mk_users-keyring-with-simple-list.patch b/queue-6.6/fscrypt-replace-mk_users-keyring-with-simple-list.patch new file mode 100644 index 0000000000..d77833ade6 --- /dev/null +++ b/queue-6.6/fscrypt-replace-mk_users-keyring-with-simple-list.patch @@ -0,0 +1,448 @@ +From 02d650b855a6ece429f82bf4893991155e53dff1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 11:30:36 -0700 +Subject: fscrypt: Replace mk_users keyring with simple list + +From: Eric Biggers + +commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. + +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 | 212 ++++++++++++++++-------------------- + 2 files changed, 113 insertions(+), 131 deletions(-) + +diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h +index 14b26036055e4..8be25746fd72e 100644 +--- a/fs/crypto/fscrypt_private.h ++++ b/fs/crypto/fscrypt_private.h +@@ -422,6 +422,19 @@ fscrypt_is_key_prepared(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 + */ +@@ -506,19 +519,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 ->mk_secret.) ++ * 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 7cbb1fd872acc..f2a0d89d5f5e1 100644 +--- a/fs/crypto/keyring.c ++++ b/fs/crypto/keyring.c +@@ -64,18 +64,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); +- key_put(mk->mk_users); +- mk->mk_users = NULL; ++ clear_mk_users(mk); + call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); + } + +@@ -145,8 +146,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 +@@ -160,30 +161,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) + { +@@ -319,91 +299,94 @@ fscrypt_find_master_key(struct super_block *sb, + return mk; + } + +-static int allocate_master_key_users_keyring(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_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; +-} ++ struct fscrypt_master_key_user *mk_user; ++ kuid_t uid = current_fsuid(); + +-/* +- * 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) +-{ +- char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; +- key_ref_t keyref; +- +- 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(sizeof(*mk_user), GFP_KERNEL); ++ 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); + } + + /* +@@ -426,13 +409,12 @@ 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); + + 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; +@@ -460,19 +442,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; +@@ -819,7 +795,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; +@@ -831,13 +806,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: +@@ -1030,16 +1002,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 +@@ -1127,6 +1101,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))) +@@ -1159,19 +1135,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: +-- +2.53.0 + diff --git a/queue-6.6/selftests-bpf-adapt-sockmap-update-error-handling.patch b/queue-6.6/selftests-bpf-adapt-sockmap-update-error-handling.patch new file mode 100644 index 0000000000..04359c2528 --- /dev/null +++ b/queue-6.6/selftests-bpf-adapt-sockmap-update-error-handling.patch @@ -0,0 +1,92 @@ +From 6d6059bf544170964a2ca38cb99a494fe19377c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 06:23:58 +0200 +Subject: selftests/bpf: Adapt sockmap update error handling + +From: Michal Luczaj + +[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ] + +Update sockmap_listen to accommodate the recent change in sockmap that +rejects unbound UDP sockets. + +TCP: Reject unbound and bound (unless established or listening). +UDP: Accept only bound sockets. + +While at it, migrate to ASSERT_* and enforce reverse xmas tree. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +index a6d8aa8c2a9ac..6386d6069b9db 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +@@ -51,8 +51,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + int family, int sotype, int mapfd) + { + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + s = xsocket(family, sotype, 0); + if (s == -1) +@@ -61,11 +61,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (sotype == SOCK_STREAM) { +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); +- } else if (err) +- FAIL_ERRNO("map_update: expected success"); ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + xclose(s); + } + +@@ -75,8 +72,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + struct sockaddr_storage addr; + socklen_t len; + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + init_addr_loopback(family, &addr, &len); + +@@ -91,8 +88,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); ++ if (sotype == SOCK_STREAM) { ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); ++ } else { ++ ASSERT_OK(err, "map_update"); ++ } + close: + xclose(s); + } +@@ -1261,7 +1262,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, + /* insert */ + TEST(test_insert_invalid), + TEST(test_insert_opened), +- TEST(test_insert_bound, SOCK_STREAM), ++ TEST(test_insert_bound), + TEST(test_insert), + /* delete */ + TEST(test_delete_after_insert), +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 74a5d66d85..8a739cbe50 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -81,3 +81,5 @@ futex-prevent-robust-futex-exit-race-some-more.patch fortify-refactor-test_fortify-makefile-to-fix-some-b.patch fortify-disable-wstringop-overread-in-tests.patch pinctrl-renesas-rzg2l-use-enotsupp-instead-of-eopnot.patch +fscrypt-replace-mk_users-keyring-with-simple-list.patch +selftests-bpf-adapt-sockmap-update-error-handling.patch diff --git a/queue-7.1/selftests-bpf-adapt-sockmap-update-error-handling.patch b/queue-7.1/selftests-bpf-adapt-sockmap-update-error-handling.patch new file mode 100644 index 0000000000..cd343a34db --- /dev/null +++ b/queue-7.1/selftests-bpf-adapt-sockmap-update-error-handling.patch @@ -0,0 +1,96 @@ +From b740be59b86ffbc418fb0858155353bce146aa68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 09:50:39 -0300 +Subject: selftests/bpf: Adapt sockmap update error handling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Michal Luczaj + +[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ] + +Update sockmap_listen to accommodate the recent change in sockmap that +rejects unbound UDP sockets. + +TCP: Reject unbound and bound (unless established or listening). +UDP: Accept only bound sockets. + +While at it, migrate to ASSERT_* and enforce reverse xmas tree. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Ricardo B. Marlière (SUSE) +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +index cc0c68bab9079..1c96a3cf4b979 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + int family, int sotype, int mapfd) + { + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + s = xsocket(family, sotype, 0); + if (s == -1) +@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (sotype == SOCK_STREAM) { +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); +- } else if (err) +- FAIL_ERRNO("map_update: expected success"); ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + xclose(s); + } + +@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + struct sockaddr_storage addr; + socklen_t len = 0; + u32 key = 0; +- u64 value; + int err, s; ++ u64 value; + + init_addr_loopback(family, &addr, &len); + +@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, + errno = 0; + value = s; + err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); +- if (!err || errno != EOPNOTSUPP) +- FAIL_ERRNO("map_update: expected EOPNOTSUPP"); ++ if (sotype == SOCK_STREAM) { ++ ASSERT_ERR(err, "map_update"); ++ ASSERT_EQ(errno, EOPNOTSUPP, "errno"); ++ } else { ++ ASSERT_OK(err, "map_update"); ++ } + close: + xclose(s); + } +@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, + /* insert */ + TEST(test_insert_invalid), + TEST(test_insert_opened), +- TEST(test_insert_bound, SOCK_STREAM), ++ TEST(test_insert_bound), + TEST(test_insert), + /* delete */ + TEST(test_delete_after_insert), +-- +2.53.0 + diff --git a/queue-7.1/selftests-bpf-ensure-udp-sockets-are-bound.patch b/queue-7.1/selftests-bpf-ensure-udp-sockets-are-bound.patch new file mode 100644 index 0000000000..e1c1c7e692 --- /dev/null +++ b/queue-7.1/selftests-bpf-ensure-udp-sockets-are-bound.patch @@ -0,0 +1,60 @@ +From f07f886e3f433ff86f4df4ea6837e39f2a410706 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Aug 2026 09:50:38 -0300 +Subject: selftests/bpf: Ensure UDP sockets are bound +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Michal Luczaj + +[ Upstream commit fe3ff273767ef22fe8a7cb3816f264927c190e50 ] + +Update sockmap_basic tests to bind sockets before they are used. This +accommodates the recent change in sockmap that rejects unbound UDP sockets. + +Signed-off-by: Michal Luczaj +Reviewed-by: Kuniyuki Iwashima +Reviewed-by: Jakub Sitnicki +Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-1-f878346f27ab@rbox.co +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Ricardo B. Marlière (SUSE) +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +index d2846579285f2..b4019d1a5575e 100644 +--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c ++++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +@@ -807,7 +807,7 @@ static void test_sockmap_many_socket(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -876,7 +876,7 @@ static void test_sockmap_many_maps(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +@@ -947,7 +947,7 @@ static void test_sockmap_same_sock(void) + return; + } + +- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); ++ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); + if (udp < 0) { + close(dgram); + close(tcp); +-- +2.53.0 + diff --git a/queue-7.1/series b/queue-7.1/series index 2fba1cc0c6..7a6fbdaf5a 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -147,3 +147,5 @@ selftests-xsk-fix-too-many-frags-multi-buffer-tx-tes.patch selftests-xsk-account-reclaimed-invalid-tx-descripto.patch bluetooth-btrtl-fix-rtl8761b-bu-broken-le-extended-s.patch bluetooth-btusb-add-tp-link-ub600-for-realtek-8761bu.patch +selftests-bpf-ensure-udp-sockets-are-bound.patch +selftests-bpf-adapt-sockmap-update-error-handling.patch