In the Linux kernel, the following vulnerability has been resolved:
landlock: Fix use-after-free of the source's parent directory
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.
[mic: Clarify the caller, reachability, and reference handling]
CVSS Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSS Score: 7.8
AV:L - The trigger is a local linkat(2) syscall: filename_linkat() -> security_path_link() -> hook_path_link() -> current_check_refer_path(), raced against local rename(2)/rmdir(2). No remote protocol supplies any of the input.
AC:L - The attacker runs every side of the race: a linkat() loop on their own file, plus rename() to reparent the source and rmdir() to free the old parent, all in directories they create (e.g. /tmp). No condition outside the attacker's control is needed; the window can be retried at will.
PR:L - An ordinary unprivileged user can enter a Landlock domain that handles any FS right (landlock_create_ruleset + landlock_restrict_self after PR_SET_NO_NEW_PRIVS, no capability needed); that makes landlock_get_applicable_subject() non-NULL and reaches the unpinned d_parent read.
UI:N - The attacker's own syscalls on files and directories they own trigger the bug; no other user or administrator has to do anything.
S:U - The corruption happens in kernel memory under the same kernel authority; exploiting it is standard local privilege escalation, not a crossing into a separate authority such as a hypervisor.
C:H - collect_domain_accesses() dereferences the freed old_parent dentry and follows its d_parent/d_inode pointers into the Landlock inode blob via find_rule(). If the attacker reallocates the freed slab, they control pointers that the kernel then follows and reads.
I:H - collect_domain_accesses() calls dget() and later dput() on the freed dentry. That writes the lockref inside the freed or reallocated object and can run dentry teardown on it, a write primitive that can lead to a double free or control-flow hijack.
A:H - The KASAN report in the commit shows a slab use-after-free in collect_domain_accesses(). On production kernels the refcount changes on freed memory or the stale pointer walk can oops or panic the kernel.
| Attack Vector |
Local |
Scope |
Unchanged |
| Attack Complexity |
Low |
Confidentiality Impact |
High |
| Privileges Required |
Low |
Integrity Impact |
High |
| User Interaction |
None |
Availability Impact |
High |
AV:L - The trigger is a local linkat(2) syscall: filename_linkat() -> security_path_link() -> hook_path_link() -> current_check_refer_path(), raced against local rename(2)/rmdir(2). No remote protocol supplies any of the input.
AC:L - The attacker runs every side of the race: a linkat() loop on their own file, plus rename() to reparent the source and rmdir() to free the old parent, all in directories they create (e.g. /tmp). No condition outside the attacker's control is needed; the window can be retried at will.
PR:L - An ordinary unprivileged user can enter a Landlock domain that handles any FS right (landlock_create_ruleset + landlock_restrict_self after PR_SET_NO_NEW_PRIVS, no capability needed); that makes landlock_get_applicable_subject() non-NULL and reaches the unpinned d_parent read.
UI:N - The attacker's own syscalls on files and directories they own trigger the bug; no other user or administrator has to do anything.
S:U - The corruption happens in kernel memory under the same kernel authority; exploiting it is standard local privilege escalation, not a crossing into a separate authority such as a hypervisor.
C:H - collect_domain_accesses() dereferences the freed old_parent dentry and follows its d_parent/d_inode pointers into the Landlock inode blob via find_rule(). If the attacker reallocates the freed slab, they control pointers that the kernel then follows and reads.
I:H - collect_domain_accesses() calls dget() and later dput() on the freed dentry. That writes the lockref inside the freed or reallocated object and can run dentry teardown on it, a write primitive that can lead to a double free or control-flow hijack.
A:H - The KASAN report in the commit shows a slab use-after-free in collect_domain_accesses(). On production kernels the refcount changes on freed memory or the stale pointer walk can oops or panic the kernel.
CVSS 3.1