Skip to content

Commit 991d40c

Browse files
author
CKI Backport Bot
committed
vfs: fix race between evice_inodes() and find_inode()&iput()
JIRA: https://issues.redhat.com/browse/RHEL-63193 CVE: CVE-2024-47679 commit 88b1afb Author: Julian Sun <[email protected]> Date: Fri Aug 23 21:07:30 2024 +0800 vfs: fix race between evice_inodes() and find_inode()&iput() Hi, all Recently I noticed a bug[1] in btrfs, after digged it into and I believe it'a race in vfs. Let's assume there's a inode (ie ino 261) with i_count 1 is called by iput(), and there's a concurrent thread calling generic_shutdown_super(). cpu0: cpu1: iput() // i_count is 1 ->spin_lock(inode) ->dec i_count to 0 ->iput_final() generic_shutdown_super() ->__inode_add_lru() ->evict_inodes() // cause some reason[2] ->if (atomic_read(inode->i_count)) continue; // return before // inode 261 passed the above check // list_lru_add_obj() // and then schedule out ->spin_unlock() // note here: the inode 261 // was still at sb list and hash list, // and I_FREEING|I_WILL_FREE was not been set btrfs_iget() // after some function calls ->find_inode() // found the above inode 261 ->spin_lock(inode) // check I_FREEING|I_WILL_FREE // and passed ->__iget() ->spin_unlock(inode) // schedule back ->spin_lock(inode) // check (I_NEW|I_FREEING|I_WILL_FREE) flags, // passed and set I_FREEING iput() ->spin_unlock(inode) ->spin_lock(inode) ->evict() // dec i_count to 0 ->iput_final() ->spin_unlock() ->evict() Now, we have two threads simultaneously evicting the same inode, which may trigger the BUG(inode->i_state & I_CLEAR) statement both within clear_inode() and iput(). To fix the bug, recheck the inode->i_count after holding i_lock. Because in the most scenarios, the first check is valid, and the overhead of spin_lock() can be reduced. If there is any misunderstanding, please let me know, thanks. [1]: https://lore.kernel.org/linux-btrfs/[email protected]/ [2]: The reason might be 1. SB_ACTIVE was removed or 2. mapping_shrinkable() return false when I reproduced the bug. Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=67ba3c42bcbb4665d3ad CC: [email protected] Fixes: 63997e9 ("split invalidate_inodes()") Signed-off-by: Julian Sun <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Signed-off-by: CKI Backport Bot <[email protected]>
1 parent 181997d commit 991d40c

File tree

1 file changed

+4
-0
lines changed

1 file changed

+4
-0
lines changed

fs/inode.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,10 @@ void evict_inodes(struct super_block *sb)
720720
continue;
721721

722722
spin_lock(&inode->i_lock);
723+
if (atomic_read(&inode->i_count)) {
724+
spin_unlock(&inode->i_lock);
725+
continue;
726+
}
723727
if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
724728
spin_unlock(&inode->i_lock);
725729
continue;

0 commit comments

Comments
 (0)