FAILED: patch "[PATCH] landlock: Fix use-after-free of the source's parent directory" failed to apply to 6.6-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 2c6dc792538260a8087ac5b22c31b3b8e47c85d6
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026091644-catfight-coerce-54c7@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 2c6dc792538260a8087ac5b22c31b3b8e47c85d6 Mon Sep 17 00:00:00 2001
From: Norbert Szetei <[email protected]>
Date: Sat, 22 Aug 2026 14:29:00 +0200
Subject: [PATCH] landlock: Fix use-after-free of the source's parent directory
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
current_check_refer_path() reads old_dentry->d_parent without holding a reference nor a lock on it, and then dereferences it in collect_domain_accesses() and in the audit record.
A reference on a child does not pin its parent: __d_move() reassigns dentry->d_parent and drops the reference the child held on its former parent. hook_path_rename() is not affected because the rename path calls lock_rename() before the hook, so the source cannot be reparented under it. hook_path_link() has no such protection: filename_linkat() holds a reference on the source dentry but neither locks nor references its parent, so a concurrent rename(2) can reparent the source while security_path_link() runs, and the former parent can then be removed and freed while the hook walks it.
A process can trigger this after entering a Landlock domain that handles at least one filesystem access right. The process can then race a linkat(2) loop against rename(2) and rmdir(2):
BUG: KASAN: slab-use-after-free in collect_domain_accesses+0x278/0x290 Read of size 4 at addr ffff888160bd53f4 by task llrepro2/549 collect_domain_accesses+0x278/0x290 current_check_refer_path+0x952/0x1120 security_path_link+0x1be/0x320 filename_linkat+0x342/0x6d0 __x64_sys_linkat+0xfa/0x150 Freed by task 562: kmem_cache_free+0x139/0x4c0 i_callback+0x4b/0x80 rcu_core+0x7dc/0x10a0
Take a reference on the dentry selected as the source parent, using dget() for the common-mount-root case and dget_parent() otherwise. Release it after the hierarchy walk and synchronous audit logging.
security/landlock/fs.c +12 -6
@@ -1298,11 +1298,12 @@ static int current_check_refer_path(struct dentry *const old_dentry,/* * old_dentry may be the root of the common mount point and * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and -* OPEN_TREE_CLONE). We do not need to call dget(old_parent) because -* we keep a reference to old_dentry. +* OPEN_TREE_CLONE). Pin the dentry used as old_parent in either case. +* Otherwise, dget_parent() safely fetches and pins the current parent +* against a concurrent rename(2). */ - old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry : - old_dentry->d_parent; + old_parent = (old_dentry == mnt_dir.dentry) ?dget (old_dentry) : +dget_parent (old_dentry);/* new_dir->dentry is equal to new_dentry->d_parent */ allow_parent1 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
@@ -1311,8 +1312,10 @@ static int current_check_refer_path(struct dentry *const old_dentry, allow_parent2 =collect_domain_accesses (subject->domain, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); -if (allow_parent1 && allow_parent2) +if (allow_parent1 && allow_parent2) { +dput (old_parent);return 0 ; + }/* * To be able to compare source and destination domain access rights,
@@ -1324,8 +1327,10 @@ static int current_check_refer_path(struct dentry *const old_dentry, subject->domain, &mnt_dir, access_request_parent1, &layer_masks_parent1, &request1, old_dentry, access_request_parent2, &layer_masks_parent2, &request2, - exchange ? new_dentry : NULL)) + exchange ? new_dentry : NULL)) { +dput (old_parent);return 0 ; + }if (request1.access) { request1.audit.u.path.dentry = old_parent;
@@ -1335,6 +1340,7 @@ static int current_check_refer_path(struct dentry *const old_dentry, request2.audit.u.path.dentry = new_dir->dentry;landlock_log_denial (subject, &request2); } +dput (old_parent);/* * This prioritizes EACCES over EXDEV for all actions, including
Sasha Levin
maintainerPATCH 1/1 landlock: Fix use-after-free of the source's parent directory
From: Norbert Szetei <[email protected]>
[ Upstream commit 2c6dc792538260a8087ac5b22c31b3b8e47c85d6 ]
current_check_refer_path() reads old_dentry->d_parent without holding a reference nor a lock on it, and then dereferences it in collect_domain_accesses() and in the audit record.
A reference on a child does not pin its parent: __d_move() reassigns dentry->d_parent and drops the reference the child held on its former parent. hook_path_rename() is not affected because the rename path calls lock_rename() before the hook, so the source cannot be reparented under it. hook_path_link() has no such protection: filename_linkat() holds a reference on the source dentry but neither locks nor references its parent, so a concurrent rename(2) can reparent the source while security_path_link() runs, and the former parent can then be removed and freed while the hook walks it.
A process can trigger this after entering a Landlock domain that handles at least one filesystem access right. The process can then race a linkat(2) loop against rename(2) and rmdir(2):
BUG: KASAN: slab-use-after-free in collect_domain_accesses+0x278/0x290 Read of size 4 at addr ffff888160bd53f4 by task llrepro2/549 collect_domain_accesses+0x278/0x290 current_check_refer_path+0x952/0x1120 security_path_link+0x1be/0x320 filename_linkat+0x342/0x6d0 __x64_sys_linkat+0xfa/0x150 Freed by task 562: kmem_cache_free+0x139/0x4c0 i_callback+0x4b/0x80 rcu_core+0x7dc/0x10a0
Take a reference on the dentry selected as the source parent, using dget() for the common-mount-root case and dget_parent() otherwise. Release it after the hierarchy walk and synchronous audit logging.
--- security/landlock/fs.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
security/landlock/fs.c +13 -6
@@ -908,11 +908,12 @@ static int current_check_refer_path(struct dentry *const old_dentry,/* * old_dentry may be the root of the common mount point and * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and -* OPEN_TREE_CLONE). We do not need to call dget(old_parent) because -* we keep a reference to old_dentry. +* OPEN_TREE_CLONE). Pin the dentry used as old_parent in either case. +* Otherwise, dget_parent() safely fetches and pins the current parent +* against a concurrent rename(2). */ - old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry : - old_dentry->d_parent; + old_parent = (old_dentry == mnt_dir.dentry) ?dget (old_dentry) : +dget_parent (old_dentry);/* new_dir->dentry is equal to new_dentry->d_parent */ allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent,
@@ -920,8 +921,10 @@ static int current_check_refer_path(struct dentry *const old_dentry, allow_parent2 =collect_domain_accesses ( dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); -if (allow_parent1 && allow_parent2) +if (allow_parent1 && allow_parent2) { +dput (old_parent);return 0 ; + }/* * To be able to compare source and destination domain access rights,
@@ -932,8 +935,12 @@ static int current_check_refer_path(struct dentry *const old_dentry,if (is_access_to_paths_allowed ( dom, &mnt_dir, access_request_parent1, &layer_masks_parent1, old_dentry, access_request_parent2, &layer_masks_parent2, - exchange ? new_dentry :NULL )) + exchange ? new_dentry :NULL )) { +dput (old_parent);return 0 ; + } + +dput (old_parent);/* * This prioritizes EACCES over EXDEV for all actions, including
Signature · 1 line
2.53.0