dm_get_device() identifies devices by their resolved dev_t, but pool_ctr() and thin_ctr() only reject identical argument strings. Different paths such as "/dev/ram0" and "1:0" therefore bypass the check and select the same dm_dev_internal.
syzbot reported a slab-use-after-free in pool_ctr() when a read-only pool used such aliased metadata and data devices. Opening the metadata device read-only first and the data device read-write second made upgrade_mode() replace dd->dm_dev and free the old table_device, leaving metadata_dev dangling before __pool_find() dereferenced metadata_dev->bdev. syzbot reported: BUG: KASAN: slab-use-after-free in pool_ctr+0x874/0xd60 drivers/md/dm-thin.c:3363 Read of size 8 at addr ffff8880340cc698 by task syz.0.17/5507 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 pool_ctr+0x874/0xd60 drivers/md/dm-thin.c:3363 dm_table_add_target+0x58d/0xc70 drivers/md/dm-table.c:749 populate_table drivers/md/dm-ioctl.c:1602 [inline] table_load+0x325/0x8a0 drivers/md/dm-ioctl.c:1645 ctl_ioctl drivers/md/dm-ioctl.c:2221 [inline] dm_ctl_ioctl+0x9ec/0xe10 drivers/md/dm-ioctl.c:2243 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 5507: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:409 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:426 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_node_noprof+0x3cc/0x620 mm/slub.c:5582 _kmalloc_node_noprof include/linux/slab.h:1196 [inline] open_table_device drivers/md/dm.c:733 [inline] dm_get_table_device+0x23e/0x730 drivers/md/dm.c:821 dm_get_device+0x327/0x760 drivers/md/dm-table.c:388 pool_ctr+0x4ae/0xd60 drivers/md/dm-thin.c:3329 dm_table_add_target+0x58d/0xc70 drivers/md/dm-table.c:749 populate_table drivers/md/dm-ioctl.c:1602 [inline] table_load+0x325/0x8a0 drivers/md/dm-ioctl.c:1645 ctl_ioctl drivers/md/dm-ioctl.c:2221 [inline] dm_ctl_ioctl+0x9ec/0xe10 drivers/md/dm-ioctl.c:2243 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 5507: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:264 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:296 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2748 [inline] slab_free mm/slub.c:6509 [inline] kfree+0x1c5/0x650 mm/slub.c:6802 close_table_device drivers/md/dm.c:798 [inline] dm_put_table_device+0x255/0x2a0 drivers/md/dm.c:841 upgrade_mode drivers/md/dm-table.c:326 [inline] dm_get_device+0x56f/0x760 drivers/md/dm-table.c:399 pool_ctr+0x585/0xd60 drivers/md/dm-thin.c:3336 dm_table_add_target+0x58d/0xc70 drivers/md/dm-table.c:749 populate_table drivers/md/dm-ioctl.c:1602 [inline] table_load+0x325/0x8a0 drivers/md/dm-ioctl.c:1645 ctl_ioctl drivers/md/dm-ioctl.c:2221 [inline] dm_ctl_ioctl+0x9ec/0xe10 drivers/md/dm-ioctl.c:2243 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff8880340cc680 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 24 bytes inside of freed 96-byte region [ffff8880340cc680, ffff8880340cc6e0) Validate the resolved device numbers with dm_devt_from_path() before either device is opened, keeping the existing string comparison. Do this in both pool_ctr() and thin_ctr(), which has the same problem for the pool and optional origin devices. Checking the device identity up front avoids the mode upgrade entirely and is preferable to switching the dm_get_device() call order, which would rely on the data mode being a superset of the metadata mode and would only cover pool_ctr(). Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=83827f9a59abe148bff4 Fixes: 70de2cbda8a5 ("dm thin: add sanity checks to thin-pool and external snapshot creation") Cc: [email protected] Signed-off-by: Drif Abdelmalek Mohamed Said <[email protected]> --- drivers/md/dm-thin.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 358ed77ffb2b..281b2e1745c3 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -3292,6 +3292,8 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv) dm_block_t low_water_blocks; struct dm_dev *metadata_dev; blk_mode_t metadata_mode; + dev_t metadata_devt; + dev_t data_devt; /* * FIXME Remove validation from scope of lock. @@ -3314,6 +3316,22 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto out_unlock; } + r = dm_devt_from_path(argv[0], &metadata_devt); + if (r) { + ti->error = "Error opening metadata block device"; + goto out_unlock; + } + r = dm_devt_from_path(argv[1], &data_devt); + if (r) { + ti->error = "Error opening metadata data device"; + goto out_unlock; + } + if (metadata_devt == data_devt) { + ti->error = "Error setting metadata or data device"; + r = -EINVAL; + goto out_unlock; + } + /* * Set default pool features. */ @@ -4204,12 +4222,30 @@ static int thin_ctr(struct dm_target *ti, unsigned int argc, char **argv) tc->sort_bio_list = RB_ROOT; if (argc == 3) { + dev_t pool_devt; + dev_t origin_devt; if (!strcmp(argv[0], argv[2])) { ti->error = "Error setting origin device"; r = -EINVAL; goto bad_origin_dev; } + r = dm_devt_from_path(argv[0], &pool_devt); + if (r) { + ti->error = "Error opening metadata block device"; + goto out_unlock; + } + r = dm_devt_from_path(argv[2], &origin_devt); + if (r) { + ti->error = "Error opening metadata data device"; + goto out_unlock; + } + if (pool_devt == origin_devt) { + ti->error = "Error setting metadata or data device"; + r = -EINVAL; + goto out_unlock; + } + r = dm_get_device(ti, argv[2], BLK_OPEN_READ, &origin_dev); if (r) { ti->error = "Error opening origin device"; -- 2.43.0
