Re: [PATCH 2/2] super: ensure valid info

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon 28-08-23 13:26:24, Christian Brauner wrote:
> 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: 2c18a63b760a ("super: wait until we passed kill super")
> Fixes: syzbot+5b64180f8d9e39d3f061@xxxxxxxxxxxxxxxxxxxxxxxxx
> Reported-by: syzbot+5b64180f8d9e39d3f061@xxxxxxxxxxxxxxxxxxxxxxxxx
> Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>

So AFAICT this fixes the UAF issues. It does reintroduce the EBUSY problems
with mount racing with umount e.g. for EROFS which calls bdev_release()
after calling kill_anon_super() but I think we can live with that until we
come up with a neater solution for this problem. So feel free to add:

Reviewed-by: Jan Kara <jack@xxxxxxx>

								Honza

> ---
>  fs/super.c | 49 ++++++++++++++++++++++++++++++-------------------
>  1 file changed, 30 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/super.c b/fs/super.c
> index 779247eb219c..ad7ac3a24d38 100644
> --- a/fs/super.c
> +++ b/fs/super.c
> @@ -434,6 +434,33 @@ void put_super(struct super_block *sb)
>  	spin_unlock(&sb_lock);
>  }
>  
> +static void kill_super_notify(struct super_block *sb)
> +{
> +	lockdep_assert_not_held(&sb->s_umount);
> +
> +	/* already notified earlier */
> +	if (sb->s_flags & SB_DEAD)
> +		return;
> +
> +	/*
> +	 * Remove it from @fs_supers so it isn't found by new
> +	 * sget{_fc}() walkers anymore. Any concurrent mounter still
> +	 * managing to grab a temporary reference is guaranteed to
> +	 * already see SB_DYING and will wait until we notify them about
> +	 * SB_DEAD.
> +	 */
> +	spin_lock(&sb_lock);
> +	hlist_del_init(&sb->s_instances);
> +	spin_unlock(&sb_lock);
> +
> +	/*
> +	 * Let concurrent mounts know that this thing is really dead.
> +	 * We don't need @sb->s_umount here as every concurrent caller
> +	 * will see SB_DYING and either discard the superblock or wait
> +	 * for SB_DEAD.
> +	 */
> +	super_wake(sb, SB_DEAD);
> +}
>  
>  /**
>   *	deactivate_locked_super	-	drop an active reference to superblock
> @@ -453,6 +480,8 @@ void deactivate_locked_super(struct super_block *s)
>  		unregister_shrinker(&s->s_shrink);
>  		fs->kill_sb(s);
>  
> +		kill_super_notify(s);
> +
>  		/*
>  		 * Since list_lru_destroy() may sleep, we cannot call it from
>  		 * put_super(), where we hold the sb_lock. Therefore we destroy
> @@ -461,25 +490,6 @@ void deactivate_locked_super(struct super_block *s)
>  		list_lru_destroy(&s->s_dentry_lru);
>  		list_lru_destroy(&s->s_inode_lru);
>  
> -		/*
> -		 * Remove it from @fs_supers so it isn't found by new
> -		 * sget{_fc}() walkers anymore. Any concurrent mounter still
> -		 * managing to grab a temporary reference is guaranteed to
> -		 * already see SB_DYING and will wait until we notify them about
> -		 * SB_DEAD.
> -		 */
> -		spin_lock(&sb_lock);
> -		hlist_del_init(&s->s_instances);
> -		spin_unlock(&sb_lock);
> -
> -		/*
> -		 * Let concurrent mounts know that this thing is really dead.
> -		 * We don't need @sb->s_umount here as every concurrent caller
> -		 * will see SB_DYING and either discard the superblock or wait
> -		 * for SB_DEAD.
> -		 */
> -		super_wake(s, SB_DEAD);
> -
>  		put_filesystem(fs);
>  		put_super(s);
>  	} else {
> @@ -1260,6 +1270,7 @@ void kill_anon_super(struct super_block *sb)
>  {
>  	dev_t dev = sb->s_dev;
>  	generic_shutdown_super(sb);
> +	kill_super_notify(sb);
>  	free_anon_bdev(dev);
>  }
>  EXPORT_SYMBOL(kill_anon_super);
> 
> -- 
> 2.34.1
> 
-- 
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux