hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8WPQW CVE: NA
--------------------------------
While run xfstests generic/429 on ext4, we get following report:
WARNING: CPU: 4 PID: 5749 at fs/dcache.c:349 dentry_free.cold+0x20/0x76 CPU: 4 PID: 5749 Comm: t_encrypted_d_r Not tainted 4.19.90-00004-g0ec2c5ee4e0 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014 RIP: 0010:dentry_free.cold+0x20/0x76 RSP: 0018:ffffbc0944a5bdf0 EFLAGS: 00010202 RAX: 0000000000000024 RBX: ffff955f06e74f30 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff955f3bb168f8 RDI: ffff955f3bb168f8 RBP: ffff955f06e4c6c0 R08: ffff955f3bb168f8 R09: 0000000000000005 R10: 0000000000000c27 R11: ffffffffb57d3bad R12: ffff955f06e4c718 R13: ffff955f06e4c718 R14: ffff955f06e74f30 R15: 0000000000000000 FS: 00007f4ba0307b40(0000) GS:ffff955f3bb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f4b9bcd1ff8 CR3: 0000000123666000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: __dentry_kill+0x219/0x340 shrink_dentry_list+0xee/0x3e0 shrink_dcache_parent+0x8c/0xc0 vfs_rmdir+0x142/0x230 do_rmdir+0x1c2/0x250 __x64_sys_rmdir+0x1b/0x30 do_syscall_64+0xcb/0x370 entry_SYSCALL_64_after_hwframe+0x5c/0xc1
Look at generic/429 test case, removing the directory and accessing the file that do not exist in the directory is done concurrently here. Consider the following concurrent call flow, which triggers the warning of negative dentry flag in dentry_free(). The root cause of the problem is that lack of lock protection when determining whether the dentry needs to make a fast put, it causes that the DCACHE_NEGATIVE_ACCOUNT flag to be set incorrectly.
rmdir stat file ------------------------- ------------------------- fast_dput if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry))
shrink_dentry_list spin_lock(&dentry->d_lock) d_shrink_del(dentry) <clear DCACHE_LRU_LIST> __dentry_kill(dentry) dentry_unlist(dentry, parent) dentry->d_flags &= ~DCACHE_NEGATIVE_ACCOUNT <clear DCACHE_NEGATIVE_ACCOUNT> spin_unlock(&dentry->d_lock)
spin_lock(&dentry->d_lock) limit_negative_dentry(dentry) <set DCACHE_NEGATIVE_ACCOUNT> spin_unlock(&dentry->d_lock)
dentry_free(dentry) WARN_ON(dentry->d_flags & DCACHE_NEGATIVE_ACCOUNT)
Fix the problem by checking the dentry is dead before set neagtive flag.
Fixes: 0f4e7166bfab ("fs:/dcache.c: fix negative dentry limit not complete problem") Signed-off-by: Long Li leo.lilong@huawei.com --- fs/dcache.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/fs/dcache.c b/fs/dcache.c index 2a7eaac0c3ef..926df08eeb0c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -643,6 +643,10 @@ static inline bool limit_negative_dentry(struct dentry *dentry) { struct dentry *parent;
+ /* The dentry is now unrecoverably dead, shuoldn't limit */ + if (unlikely(dentry->d_lockref.count < 0)) + return false; + parent = dentry->d_parent; if (unlikely(!parent)) return false;