Re: [PATCH v1] btrfs: wait for a grace period before freeing a new device
From: Daniel Vacek
Date: Wed Sep 30 2026 - 12:23:31 EST
On Wed, 30 Sept 2026 at 01:29, Qu Wenruo <quwenruo.btrfs@xxxxxxx> wrote:
> 在 2026/9/29 22:06, Binbin Deng 写道:
> > KASAN reports a slab-use-after-free in btrfs_statfs() walking
> > fs_devices->devices. btrfs_init_new_device() publishes the new device
> > with list_add_rcu() and can fail afterwards, for example with -ENOSPC
> > in init_first_rw_device() when a user with CAP_SYS_ADMIN adds a device
> > that is too small to a seeding filesystem mounted read-only. The error
> > path removes the entry with list_del_rcu() and frees the object in
> > btrfs_free_device() without waiting for a grace period, while
> > btrfs_calc_avail_data_space() and btrfs_ioctl_fs_info() keep reading
> > the device list under rcu_read_lock().
> >
> > BUG: KASAN: slab-use-after-free in btrfs_statfs+0x11af/0x14a0
> > Read of size 8 at addr ffff8880096be858 by task poc/252
> > Call Trace:
> > <TASK>
> > dump_stack_lvl+0x53/0x70
> > print_report+0xd0/0x630
> > ? __pfx__raw_spin_lock_irqsave+0x10/0x10
> > ? filename_lookup+0x1a9/0x540
> > ? btrfs_statfs+0x11af/0x14a0
> > kasan_report+0xce/0x100
> > ? btrfs_statfs+0x11af/0x14a0
> > btrfs_statfs+0x11af/0x14a0
> > statfs_by_dentry+0x117/0x1e0
> > user_statfs+0xac/0x130
> > ? __pfx_user_statfs+0x10/0x10
> > __do_sys_statfs+0x80/0xe0
> > ? __pfx___do_sys_statfs+0x10/0x10
> > do_syscall_64+0xf9/0x540
> > entry_SYSCALL_64_after_hwframe+0x77/0x7f
> >
> > Allocated by task 256:
> > kasan_save_stack+0x33/0x60
> > kasan_save_track+0x14/0x30
> > __kasan_kmalloc+0x8f/0xa0
> > __kmalloc_cache_noprof+0x158/0x370
> > btrfs_alloc_device+0xad/0x3e0
> > btrfs_init_new_device+0x3e8/0x36e0
> > btrfs_ioctl+0x150d/0x6c20
> > __x64_sys_ioctl+0x134/0x1c0
> > do_syscall_64+0xf9/0x540
> > entry_SYSCALL_64_after_hwframe+0x77/0x7f
> >
> > Freed by task 256:
> > kasan_save_stack+0x33/0x60
> > kasan_save_track+0x14/0x30
> > kasan_save_free_info+0x3b/0x60
> > __kasan_slab_free+0x43/0x70
> > kfree+0x121/0x380
> > btrfs_init_new_device+0x4a0/0x36e0
> > btrfs_ioctl+0x150d/0x6c20
> > __x64_sys_ioctl+0x134/0x1c0
> > do_syscall_64+0xf9/0x540
> > entry_SYSCALL_64_after_hwframe+0x77/0x7f
> >
> > Fix by waiting for an RCU grace period after the device is removed
> > from the list and before it is freed.
> >
> > Fixes: 1f78160ce1b1 ("Btrfs: using rcu lock in the reader side of devices list")
> > Signed-off-by: Binbin Deng <18983559317@xxxxxxx>
> > ---
> > fs/btrfs/volumes.c | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> > index 85ea9c5d4536..2693dacd1da7 100644
> > --- a/fs/btrfs/volumes.c
> > +++ b/fs/btrfs/volumes.c
> > @@ -3168,6 +3168,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
> > btrfs_update_per_profile_avail(fs_info);
> > mutex_unlock(&fs_info->chunk_mutex);
> > mutex_unlock(&fs_info->fs_devices->device_list_mutex);
> > + synchronize_rcu();
>
> Please follow all other call sites where synchronize_rcu() is called
> just before btrfs_free_device().
This call site is unlike the others. The device is allocated and
initialized here.
There's no need to sync with RCU until the device has been published;
i.e. the other error paths are perfectly fine to just free the device
right away.
Placing the synchronize_rcu() after dropping the mutexes and before
the `error_trans` label is exactly the right spot in this function.
We should really be serious about needless RCU syncs.
--nX
> > error_trans:
> > if (trans)
> > btrfs_end_transaction(trans);