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+…
[View More]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: e91cbe162d54 ("fs:/dcache.c: fix negative dentry limit not complete problem")
Signed-off-by: Long Li <leo.lilong(a)huawei.com>
---
fs/dcache.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/dcache.c b/fs/dcache.c
index 3cf6fadd5550..cc5ba31d9b95 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -654,6 +654,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;
--
2.31.1
[View Less]
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+…
[View More]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(a)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;
--
2.31.1
[View Less]