CVE-2026-46160
Description
In the Linux kernel, the following vulnerability has been resolved:
btrfs: fix missing last_unlink_trans update when removing a directory
When removing a directory we are not updating its last_unlink_trans field, which can result in incorrect fsync behaviour in case some one fsyncs the directory after it was removed because it's holding a file descriptor on it.
Example scenario:
mkdir /mnt/dir1 mkdir /mnt/dir1/dir2 mkdir /mnt/dir3
sync -f /mnt
# Do some change to the directory and fsync it. chmod 700 /mnt/dir1 xfs_io -c fsync /mnt/dir1
# Move dir2 out of dir1 so that dir1 becomes empty. mv /mnt/dir1/dir2 /mnt/dir3/
open fd on /mnt/dir1 call rmdir(2) on path "/mnt/dir1" fsync fd
When attempting to mount the filesystem, the log replay will fail with an -EIO error and dmesg/syslog has the following:
[445771.626482] BTRFS info (device dm-0): first mount of filesystem 0368bbea-6c5e-44b5-b409-09abe496e650 [445771.626486] BTRFS info (device dm-0): using crc32c checksum algorithm [445771.627912] BTRFS info (device dm-0): start tree-log replay [445771.628335] page: refcount:2 mapcount:0 mapping:0000000061443ddc index:0x1d00 pfn:0x7072a5 [445771.629453] memcg:ffff89f400351b00 [445771.629892] aops:btree_aops [btrfs] ino:1 [445771.630737] flags: 0x17fffc00000402a(uptodate|lru|private|writeback|node=0|zone=2|lastcpupid=0x1ffff) [445771.632359] raw: 017fffc00000402a fffff47284d950c8 fffff472907b7c08 ffff89f458e412b8 [445771.633713] raw: 0000000000001d00 ffff89f6c51d1a90 00000002ffffffff ffff89f400351b00 [445771.635029] page dumped because: eb page dump [445771.635825] BTRFS critical (device dm-0): corrupt leaf: root=5 block=30408704 slot=10 ino=258, invalid nlink: has 2 expect no more than 1 for dir [445771.638088] BTRFS info (device dm-0): leaf 30408704 gen 10 total ptrs 17 free space 14878 owner 5 [445771.638091] BTRFS info (device dm-0): refs 4 lock_owner 0 current 3581087 [445771.638094] item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160 [445771.638097] inode generation 3 transid 9 size 16 nbytes 16384 [445771.638098] block group 0 mode 40755 links 1 uid 0 gid 0 [445771.638100] rdev 0 sequence 2 flags 0x0 [445771.638102] atime 1775744884.0 [445771.660056] ctime 1775744885.645502983 [445771.660058] mtime 1775744885.645502983 [445771.660060] otime 1775744884.0 [445771.660062] item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12 [445771.660064] index 0 name_len 2 [445771.660066] item 2 key (256 DIR_ITEM 1843588421) itemoff 16077 itemsize 34 [445771.660068] location key (259 1 0) type 2 [445771.660070] transid 9 data_len 0 name_len 4 [445771.660075] item 3 key (256 DIR_ITEM 2363071922) itemoff 16043 itemsize 34 [445771.660076] location key (257 1 0) type 2 [445771.660077] transid 9 data_len 0 name_len 4 [445771.660078] item 4 key (256 DIR_INDEX 2) itemoff 16009 itemsize 34 [445771.660079] location key (257 1 0) type 2 [445771.660080] transid 9 data_len 0 name_len 4 [445771.660081] item 5 key (256 DIR_INDEX 3) itemoff 15975 itemsize 34 [445771.660082] location key (259 1 0) type 2 [445771.660083] transid 9 data_len 0 name_len 4 [445771.660084] item 6 key (257 INODE_ITEM 0) itemoff 15815 itemsize 160 [445771.660086] inode generation 9 transid 9 size 8 nbytes 0 [445771.660087] block group 0 mode 40777 links 1 uid 0 gid 0 [445771.660088] rdev 0 sequence 2 flags 0x0 [445771.660089] atime 1775744885.641174097 [445771.660090] ctime 1775744885.645502983 [445771.660091] mtime 1775744885.645502983 [445771.660105] otime 1775744885.641174097 [445771.660106] item 7 key (257 INODE_REF 256) itemoff 15801 itemsize 14 [445771.660107] index 2 name_len 4 [445771.660108] item 8 key (257 DIR_ITEM 2676584006) itemoff 15767 itemsize 34 [445771.660109] location key (2 ---truncated---
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A missing last_unlink_trans update in btrfs can cause incorrect fsync behavior and log replay failure when removing a directory.
Vulnerability
In the Linux kernel's btrfs filesystem, when removing a directory (via the rmdir(2) system call), the code fails to update the directory's last_unlink_trans field. This missing update can lead to incorrect fsync behavior: if a process holds a file descriptor on the directory, performs an fsync after the removal, and a power failure occurs, the file system log replay will detect a corrupt leaf with an invalid nlink count for the directory (expecting at most 1 for a dir but finding 2). The example given shows this occurring during log replay, resulting in an -EIO error. The vulnerability affects the Linux kernel; the fix commit is referenced as fb388eb58c1ba047ccabc33901839acfecadcf49.
Exploitation
An attacker requires write access to a btrfs-mounted filesystem and the ability to create and manipulate directories. The exploit scenario involves a user (or process) that can: 1) create a directory hierarchy, 2) set up a file descriptor on a parent directory, 3) move all children out so the directory becomes empty, 4) call rmdir(2) on that directory, and 5) fsync the still-open file descriptor. If a power failure occurs before the log is properly committed, the next mount will fail due to corrupted log replay. No special network position or race window is required beyond the ability to perform these file operations.
Impact
Successful exploitation leads to filesystem corruption that prevents mounting a btrfs volume. After the power failure and subsequent mount attempt, the log replay fails with an -EIO error, making the filesystem inaccessible. This constitutes a denial of service (availability impact) as the system cannot mount the filesystem, potentially causing data loss or requiring manual recovery (e.g., btrfs check). There is no code execution or privilege escalation; the impact is strictly availability.
Mitigation
The fix is present in the upstream Linux kernel commit fb388eb58c1ba047ccabc33901839acfecadcf49 [1]. Users should apply the patch or update to a kernel version that includes this commit. No workaround is documented in the available references; the vulnerability is resolved by the kernel patch.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10cc3c0a0f9657btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f91f3b42ff4579..7add1a019d8578 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4681,6 +4681,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
aa9c3ecaf733btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cb3bdba719b15f..90375f90c9e299 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4801,6 +4801,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
999757231c49btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 40474014c03f13..55133a36430507 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4959,6 +4959,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
36fcc2c7517fbtrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cddce09cf9813d..2ad2d503e79afc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4967,6 +4967,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
fb388eb58c1bbtrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7fe868a6a51b49..feaa6de8a90f2a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4826,6 +4826,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
cc3c0a0f9657btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f91f3b42ff4579..7add1a019d8578 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4681,6 +4681,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
aa9c3ecaf733btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cb3bdba719b15f..90375f90c9e299 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4801,6 +4801,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
fb388eb58c1bbtrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7fe868a6a51b49..feaa6de8a90f2a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4826,6 +4826,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
999757231c49btrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 40474014c03f13..55133a36430507 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4959,6 +4959,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
36fcc2c7517fbtrfs: fix missing last_unlink_trans update when removing a directory
1 file changed · +2 −1
fs/btrfs/inode.c+2 −1 modifieddiff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cddce09cf9813d..2ad2d503e79afc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4967,6 +4967,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing call to btrfs_record_unlink_dir() in btrfs_rmdir() leaves the directory's last_unlink_trans field stale, causing the fsync log to record an incorrect link count for the removed directory."
Attack vector
An attacker with local filesystem access can trigger this bug by holding an open file descriptor on a directory, moving all children out of it so it becomes empty, then calling rmdir(2) on the path followed by fsync(2) on the fd [patch_id=2898177]. After a power failure, the Btrfs log replay finds the directory inode with an nlink of 2 (the stale value) instead of the expected 0 or 1, causing a corrupt-leaf error and an -EIO mount failure [patch_id=2898177]. The attacker needs only local unprivileged access to a Btrfs filesystem and the ability to create/move/remove directories and issue fsync.
Affected code
The vulnerable code is in the btrfs_rmdir() function in fs/btrfs/inode.c [patch_id=2898177]. The function was missing a call to btrfs_record_unlink_dir() before calling btrfs_unlink_inode(), so the directory inode's last_unlink_trans field was never updated during rmdir(2) operations.
What the fix does
The patch adds a single call to btrfs_record_unlink_dir(trans, dir, inode, false) in the btrfs_rmdir() function, placed just before btrfs_unlink_inode() [patch_id=2898177]. This ensures that when a directory is removed, its last_unlink_trans field is updated to the current transaction, which the fsync log replay code uses to correctly determine the directory's link count. Without this update, the log replay sees an nlink of 2 (the pre-rmdir value) and rejects the metadata as corrupt [patch_id=2898177].
Preconditions
- authAttacker must have local access to a Btrfs filesystem with permission to create, move, and remove directories and issue fsync.
- inputAttacker must hold an open file descriptor on a directory that is being removed via rmdir(2).
- configA power failure or crash must occur after the fsync but before the log is cleanly committed.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/36fcc2c7517f8a86379154c9793f867592aa8b7envd
- git.kernel.org/stable/c/999757231c49376cd1a37308d2c8c4c9932571e1nvd
- git.kernel.org/stable/c/aa9c3ecaf7337df3a689318584f879b5339ede0fnvd
- git.kernel.org/stable/c/cc3c0a0f965754ce230d93ba44ee5b34fbe6138anvd
- git.kernel.org/stable/c/fb388eb58c1ba047ccabc33901839acfecadcf49nvd
News mentions
0No linked articles in our index yet.