fs/isofs/inode.c | 1 + 1 file changed, 1 insertion(+)
When open_by_handle_at() is used with iso9660 filesystems, exportfs
creates disconnected dentries during file handle resolution. If the
operation fails (e.g., with -ESTALE during reconnect_path()), these
dentries remain cached with their associated inodes.
During unmount, shrink_dcache_for_umount() does not fully evict these
disconnected dentries, leaving their inodes with non-zero reference
counts. This triggers the "VFS: Busy inodes after unmount" warning
and causes inode leaks that accumulate across mount/unmount cycles.
The issue occurs because:
1. open_by_handle_at() calls exportfs_decode_fh_raw() to resolve
file handles
2. For iso9660 with Joliet extensions, this creates disconnected
dentries for both primary (iso9660) and secondary (Joliet) root
inodes
3. When path reconnection fails with -ESTALE, the dentries are left
in DCACHE_DISCONNECTED state
4. shrink_dcache_for_umount() in generic_shutdown_super() does not
aggressively evict these disconnected dentries
5. The associated inodes (typically root inodes 1792 and 1807)
remain with i_count=1, triggering the busy inode check
Add explicit shrink_dcache_sb() call in isofs_put_super() to ensure
all cached dentries, including disconnected ones created by exportfs
operations, are released before the superblock is destroyed.
Reported-by: syzbot+1d79ebe5383fc016cf07@syzkaller.appspotmail.com
Tested-by: syzbot+1d79ebe5383fc016cf07@syzkaller.appspotmail.com
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
fs/isofs/inode.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 6f0e6b19383c..bee410705442 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -52,6 +52,7 @@ static int isofs_dentry_cmp_ms(const struct dentry *dentry,
static void isofs_put_super(struct super_block *sb)
{
struct isofs_sb_info *sbi = ISOFS_SB(sb);
+ shrink_dcache_sb(sb);
#ifdef CONFIG_JOLIET
unload_nls(sbi->s_nls_iocharset);
--
2.43.0
On Wed 01-10-25 15:13:10, Deepanshu Kartikey wrote: > When open_by_handle_at() is used with iso9660 filesystems, exportfs > creates disconnected dentries during file handle resolution. If the > operation fails (e.g., with -ESTALE during reconnect_path()), these > dentries remain cached with their associated inodes. > > During unmount, shrink_dcache_for_umount() does not fully evict these > disconnected dentries, leaving their inodes with non-zero reference > counts. This triggers the "VFS: Busy inodes after unmount" warning > and causes inode leaks that accumulate across mount/unmount cycles. > > The issue occurs because: > 1. open_by_handle_at() calls exportfs_decode_fh_raw() to resolve > file handles > 2. For iso9660 with Joliet extensions, this creates disconnected > dentries for both primary (iso9660) and secondary (Joliet) root > inodes > 3. When path reconnection fails with -ESTALE, the dentries are left > in DCACHE_DISCONNECTED state True, but when reconnection fails, exportfs_decode_fh_raw() calls dput() on the created dentry and dput() immediately destroys DCACHE_DISCONNECTED dentries. So I'm not following how these dentries could still survive until umount(). Can you please explain? > 4. shrink_dcache_for_umount() in generic_shutdown_super() does not > aggressively evict these disconnected dentries > 5. The associated inodes (typically root inodes 1792 and 1807) > remain with i_count=1, triggering the busy inode check > > Add explicit shrink_dcache_sb() call in isofs_put_super() to ensure > all cached dentries, including disconnected ones created by exportfs > operations, are released before the superblock is destroyed. This is almost certainly a wrong way of fixing the problem. First we need to better understand why DCACHE_DISCONNECTED aren't getting properly evicted... Honza > > Reported-by: syzbot+1d79ebe5383fc016cf07@syzkaller.appspotmail.com > Tested-by: syzbot+1d79ebe5383fc016cf07@syzkaller.appspotmail.com > Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> > --- > fs/isofs/inode.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c > index 6f0e6b19383c..bee410705442 100644 > --- a/fs/isofs/inode.c > +++ b/fs/isofs/inode.c > @@ -52,6 +52,7 @@ static int isofs_dentry_cmp_ms(const struct dentry *dentry, > static void isofs_put_super(struct super_block *sb) > { > struct isofs_sb_info *sbi = ISOFS_SB(sb); > + shrink_dcache_sb(sb); > > #ifdef CONFIG_JOLIET > unload_nls(sbi->s_nls_iocharset); > -- > 2.43.0 > -- Jan Kara <jack@suse.com> SUSE Labs, CR
© 2016 - 2025 Red Hat, Inc.