Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ublk: fix use-after-free in ublk_partition_scan_work

A race condition exists between the async partition scan work and device
teardown that can lead to a use-after-free of ub->ub_disk:

1. ublk_ctrl_start_dev() schedules partition_scan_work after add_disk()
2. ublk_stop_dev() calls ublk_stop_dev_unlocked() which does:
- del_gendisk(ub->ub_disk)
- ublk_detach_disk() sets ub->ub_disk = NULL
- put_disk() which may free the disk
3. The worker ublk_partition_scan_work() then dereferences ub->ub_disk
leading to UAF

Fix this by using ublk_get_disk()/ublk_put_disk() in the worker to hold
a reference to the disk during the partition scan. The spinlock in
ublk_get_disk() synchronizes with ublk_detach_disk() ensuring the worker
either gets a valid reference or sees NULL and exits early.

Also change flush_work() to cancel_work_sync() to avoid running the
partition scan work unnecessarily when the disk is already detached.

Fixes: 7fc4da6a304b ("ublk: scan partition in async way")
Reported-by: Ruikai Peng <ruikai@pwno.io>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
f0d385f6 9670db22

+22 -15
+22 -15
drivers/block/ublk_drv.c
··· 255 255 u16 q_id, u16 tag, struct ublk_io *io, size_t offset); 256 256 static inline unsigned int ublk_req_build_flags(struct request *req); 257 257 258 - static void ublk_partition_scan_work(struct work_struct *work) 259 - { 260 - struct ublk_device *ub = 261 - container_of(work, struct ublk_device, partition_scan_work); 262 - 263 - if (WARN_ON_ONCE(!test_and_clear_bit(GD_SUPPRESS_PART_SCAN, 264 - &ub->ub_disk->state))) 265 - return; 266 - 267 - mutex_lock(&ub->ub_disk->open_mutex); 268 - bdev_disk_changed(ub->ub_disk, false); 269 - mutex_unlock(&ub->ub_disk->open_mutex); 270 - } 271 - 272 258 static inline struct ublksrv_io_desc * 273 259 ublk_get_iod(const struct ublk_queue *ubq, unsigned tag) 274 260 { ··· 1583 1597 put_device(disk_to_dev(disk)); 1584 1598 } 1585 1599 1600 + static void ublk_partition_scan_work(struct work_struct *work) 1601 + { 1602 + struct ublk_device *ub = 1603 + container_of(work, struct ublk_device, partition_scan_work); 1604 + /* Hold disk reference to prevent UAF during concurrent teardown */ 1605 + struct gendisk *disk = ublk_get_disk(ub); 1606 + 1607 + if (!disk) 1608 + return; 1609 + 1610 + if (WARN_ON_ONCE(!test_and_clear_bit(GD_SUPPRESS_PART_SCAN, 1611 + &disk->state))) 1612 + goto out; 1613 + 1614 + mutex_lock(&disk->open_mutex); 1615 + bdev_disk_changed(disk, false); 1616 + mutex_unlock(&disk->open_mutex); 1617 + out: 1618 + ublk_put_disk(disk); 1619 + } 1620 + 1586 1621 /* 1587 1622 * Use this function to ensure that ->canceling is consistently set for 1588 1623 * the device and all queues. Do not set these flags directly. ··· 2048 2041 mutex_lock(&ub->mutex); 2049 2042 ublk_stop_dev_unlocked(ub); 2050 2043 mutex_unlock(&ub->mutex); 2051 - flush_work(&ub->partition_scan_work); 2044 + cancel_work_sync(&ub->partition_scan_work); 2052 2045 ublk_cancel_dev(ub); 2053 2046 } 2054 2047