Skip to content

Commit 1d2c8bb

Browse files
adam900710kdave
authored andcommitted
btrfs: fix a use-after-free race if btrfs_open_devices() failed
[BUG] With the latest v5 version patchset "btrfs: use fs_holder_ops for btrfs" merged into linux-next, syzbot reported an use-after-free: ================================================================== BUG: KASAN: slab-use-after-free in close_fs_devices+0x81f/0x870 fs/btrfs/volumes.c:1182 Read of size 4 at addr ffff88802fe14930 by task syz.4.616/8589 CPU: 0 UID: 0 PID: 8589 Comm: syz.4.616 Not tainted 6.16.0-rc3-next-20250626-syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/07/2025 Call Trace: <TASK> dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:408 [inline] print_report+0xd2/0x2b0 mm/kasan/report.c:521 kasan_report+0x118/0x150 mm/kasan/report.c:634 close_fs_devices+0x81f/0x870 fs/btrfs/volumes.c:1182 btrfs_close_devices+0xc5/0x560 fs/btrfs/volumes.c:1201 btrfs_free_fs_info+0x4f/0x3c0 fs/btrfs/disk-io.c:1250 deactivate_locked_super+0xbc/0x130 fs/super.c:474 btrfs_get_tree_super fs/btrfs/super.c:-1 [inline] btrfs_get_tree_subvol fs/btrfs/super.c:2073 [inline] btrfs_get_tree+0xd1e/0x17f0 fs/btrfs/super.c:2107 vfs_get_tree+0x92/0x2b0 fs/super.c:1804 do_new_mount+0x24a/0xa40 fs/namespace.c:3902 do_mount fs/namespace.c:4239 [inline] __do_sys_mount fs/namespace.c:4450 [inline] __se_sys_mount+0x317/0x410 fs/namespace.c:4427 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fedccd900ca Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb a6 e8 de 1a 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fedcdc28e68 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5 RAX: ffffffffffffffda RBX: 00007fedcdc28ef0 RCX: 00007fedccd900ca RDX: 00002000000055c0 RSI: 0000200000005600 RDI: 00007fedcdc28eb0 RBP: 00002000000055c0 R08: 00007fedcdc28ef0 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000200000005600 R13: 00007fedcdc28eb0 R14: 000000000000559d R15: 0000200000000440 </TASK> Allocated by task 8589: kasan_save_stack mm/kasan/common.c:47 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:68 poison_kmalloc_redzone mm/kasan/common.c:377 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:394 kasan_kmalloc include/linux/kasan.h:260 [inline] __kmalloc_cache_noprof+0x230/0x3d0 mm/slub.c:4396 kmalloc_noprof include/linux/slab.h:905 [inline] kzalloc_noprof include/linux/slab.h:1039 [inline] alloc_fs_devices+0x4f/0x1d0 fs/btrfs/volumes.c:384 device_list_add+0x6b7/0x20b0 fs/btrfs/volumes.c:813 btrfs_scan_one_device+0x3fd/0x5b0 fs/btrfs/volumes.c:1487 btrfs_get_tree_super fs/btrfs/super.c:1856 [inline] btrfs_get_tree_subvol fs/btrfs/super.c:2073 [inline] btrfs_get_tree+0x433/0x17f0 fs/btrfs/super.c:2107 vfs_get_tree+0x92/0x2b0 fs/super.c:1804 do_new_mount+0x24a/0xa40 fs/namespace.c:3902 do_mount fs/namespace.c:4239 [inline] __do_sys_mount fs/namespace.c:4450 [inline] __se_sys_mount+0x317/0x410 fs/namespace.c:4427 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 7454: kasan_save_stack mm/kasan/common.c:47 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:68 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:576 poison_slab_object mm/kasan/common.c:247 [inline] __kasan_slab_free+0x62/0x70 mm/kasan/common.c:264 kasan_slab_free include/linux/kasan.h:233 [inline] slab_free_hook mm/slub.c:2417 [inline] slab_free mm/slub.c:4680 [inline] kfree+0x18e/0x440 mm/slub.c:4879 btrfs_free_stale_devices+0x61c/0x6b0 fs/btrfs/volumes.c:564 btrfs_scan_one_device+0x3d5/0x5b0 fs/btrfs/volumes.c:1481 btrfs_control_ioctl+0x11f/0x360 fs/btrfs/super.c:2256 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:907 [inline] __se_sys_ioctl+0xf9/0x170 fs/ioctl.c:893 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f [CAUSE] The patch "btrfs: delay btrfs_open_devices() until super block is created" changed the timing when btrfs_open_devices() is called. Now it's called after sget_fc(), the changed timing is required for using super blocks as bdev holder. The problem is the changed error handling. Before the change if btrfs_open_devices() failed we error out directly, but now we need to call deactivate_locked_super(). However since btrfs_open_devices() failed, fs_devices->opened is still 0, meaning any reclaim request can free the fs_devices. But at the same time, deactivate_locked_super() will also cleanup the fs_info with btrfs_free_fs_info(), which calls btrfs_close_devices(). This leads to the following race: Mount process | Scan process ----------------------------------+-------------------------------- btrfs_get_tree_super() | |- mutex_lock(&uuid_mutex) | |- btrfs_open_devices() | | Which failed. | | fs_devices->opened is still 0. | |- mutex_unlock(&uuid_mutex) | | | btrfs_control_ioctl() | | |- btrfs_scan_one_device() | | |- btrfs_free_stale_devices() | | That fs_devices is freed |- deactivate_locked_super() | |- btrfs_free_fs_info() | |- btrfs_close_devices() Now try to free the same fs_devices that is freed by the scan process. [FIX] If btrfs_open_devices() failed, we should not keep a pointer to it, as it can be freed at any time after uuid_mutex unlocked. So add an extra handling for btrfs_open_devices() to reset fs_info->fs_devices to NULL. This will be folded into the patch "btrfs: delay btrfs_open_devices() until super block is created". Reported-by: [email protected] Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent c4f6252 commit 1d2c8bb

File tree

1 file changed

+11
-0
lines changed

1 file changed

+11
-0
lines changed

fs/btrfs/super.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,17 @@ static int btrfs_get_tree_super(struct fs_context *fc)
19211921
mutex_lock(&uuid_mutex);
19221922
btrfs_fs_devices_dec_holding(fs_devices);
19231923
ret = btrfs_open_devices(fs_devices, mode, sb);
1924+
/*
1925+
* If btrfs_open_devices() failed, fs_devices is not opened and
1926+
* can be freed by any reclaim request after uuid_mutex unlocked.
1927+
*
1928+
* But our fs_info is still using that fs_devices, thus it will
1929+
* lead to use-after-free later.
1930+
*
1931+
* So here we must not use that fs_devices after open failure.
1932+
*/
1933+
if (ret < 0)
1934+
fs_info->fs_devices = NULL;
19241935
mutex_unlock(&uuid_mutex);
19251936
if (ret < 0) {
19261937
deactivate_locked_super(sb);

0 commit comments

Comments
 (0)