Skip to content

Commit dc3216b

Browse files
committed
super: ensure valid info
For keyed filesystems that recycle superblocks based on s_fs_info or information contained therein s_fs_info must be kept as long as the superblock is on the filesystem type super list. This isn't guaranteed as s_fs_info will be freed latest in sb->kill_sb(). The fix is simply to perform notification and list removal in kill_anon_super(). Any filesystem needs to free s_fs_info after they call the kill_*() helpers. If they don't they risk use-after-free right now so fixing it here is guaranteed that s_fs_info remain valid. For block backed filesystems notifying in pass sb->kill_sb() in deactivate_locked_super() remains unproblematic and is required because multiple other block devices can be shut down after kill_block_super() has been called from a filesystem's sb->kill_sb() handler. For example, ext4 and xfs close additional devices. Block based filesystems don't depend on s_fs_info (btrfs does use s_fs_info but also uses kill_anon_super() and not kill_block_super().). Sorry for that braino. Goal should be to unify this behavior during this cycle obviously. But let's please do a simple bugfix now. Fixes: 2c18a63 ("super: wait until we passed kill super") Fixes: syzbot+5b64180f8d9e39d3f061@syzkaller.appspotmail.com Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Christoph Hellwig <hch@lst.de> Reported-by: syzbot+5b64180f8d9e39d3f061@syzkaller.appspotmail.com Message-Id: <20230828-vfs-super-fixes-v1-2-b37a4a04a88f@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 345a5c4 commit dc3216b

File tree

1 file changed

+30
-19
lines changed

1 file changed

+30
-19
lines changed

fs/super.c

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,33 @@ void put_super(struct super_block *sb)
434434
spin_unlock(&sb_lock);
435435
}
436436

437+
static void kill_super_notify(struct super_block *sb)
438+
{
439+
lockdep_assert_not_held(&sb->s_umount);
440+
441+
/* already notified earlier */
442+
if (sb->s_flags & SB_DEAD)
443+
return;
444+
445+
/*
446+
* Remove it from @fs_supers so it isn't found by new
447+
* sget{_fc}() walkers anymore. Any concurrent mounter still
448+
* managing to grab a temporary reference is guaranteed to
449+
* already see SB_DYING and will wait until we notify them about
450+
* SB_DEAD.
451+
*/
452+
spin_lock(&sb_lock);
453+
hlist_del_init(&sb->s_instances);
454+
spin_unlock(&sb_lock);
455+
456+
/*
457+
* Let concurrent mounts know that this thing is really dead.
458+
* We don't need @sb->s_umount here as every concurrent caller
459+
* will see SB_DYING and either discard the superblock or wait
460+
* for SB_DEAD.
461+
*/
462+
super_wake(sb, SB_DEAD);
463+
}
437464

438465
/**
439466
* deactivate_locked_super - drop an active reference to superblock
@@ -453,6 +480,8 @@ void deactivate_locked_super(struct super_block *s)
453480
unregister_shrinker(&s->s_shrink);
454481
fs->kill_sb(s);
455482

483+
kill_super_notify(s);
484+
456485
/*
457486
* Since list_lru_destroy() may sleep, we cannot call it from
458487
* put_super(), where we hold the sb_lock. Therefore we destroy
@@ -461,25 +490,6 @@ void deactivate_locked_super(struct super_block *s)
461490
list_lru_destroy(&s->s_dentry_lru);
462491
list_lru_destroy(&s->s_inode_lru);
463492

464-
/*
465-
* Remove it from @fs_supers so it isn't found by new
466-
* sget{_fc}() walkers anymore. Any concurrent mounter still
467-
* managing to grab a temporary reference is guaranteed to
468-
* already see SB_DYING and will wait until we notify them about
469-
* SB_DEAD.
470-
*/
471-
spin_lock(&sb_lock);
472-
hlist_del_init(&s->s_instances);
473-
spin_unlock(&sb_lock);
474-
475-
/*
476-
* Let concurrent mounts know that this thing is really dead.
477-
* We don't need @sb->s_umount here as every concurrent caller
478-
* will see SB_DYING and either discard the superblock or wait
479-
* for SB_DEAD.
480-
*/
481-
super_wake(s, SB_DEAD);
482-
483493
put_filesystem(fs);
484494
put_super(s);
485495
} else {
@@ -1260,6 +1270,7 @@ void kill_anon_super(struct super_block *sb)
12601270
{
12611271
dev_t dev = sb->s_dev;
12621272
generic_shutdown_super(sb);
1273+
kill_super_notify(sb);
12631274
free_anon_bdev(dev);
12641275
}
12651276
EXPORT_SYMBOL(kill_anon_super);

0 commit comments

Comments
 (0)