block: fix use-after-free of the disk in sync_bdevs()

sync_bdevs() holds a reference to the bdev inode while it takes
bd_disk->open_mutex. For a partition the inode doesn't keep the gendisk
alive, the device reference does: part_release() drops the disk as soon
as the last reference to the partition device is gone. A sync(2) racing
with the last close of a partition of an already deleted disk locks a
freed open_mutex.

Take a device reference before touching the disk, like
blkdev_get_no_open() does, and skip the bdev if it's on its way out.

Fixes: a8698707a1 ("block: move bd_mutex to struct gendisk")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20261028164415.1262013-5-axboe@kernel.dk
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Josef Bacik 2026-10-28 10:41:07 -06:00 committed by Jens Axboe
parent 045cbfc369
commit cebb1c2331

View File

@ -1366,6 +1366,15 @@ void sync_bdevs(bool wait)
old_inode = inode;
bdev = I_BDEV(inode);
/*
* The inode does not keep the gendisk of a partition alive, the
* device does.
*/
if (!kobject_get_unless_zero(&bdev->bd_device.kobj)) {
spin_lock(&blockdev_superblock->s_inode_list_lock);
continue;
}
mutex_lock(&bdev->bd_disk->open_mutex);
if (!atomic_read(&bdev->bd_openers)) {
; /* skip */
@ -1381,6 +1390,7 @@ void sync_bdevs(bool wait)
filemap_fdatawrite(inode->i_mapping);
}
mutex_unlock(&bdev->bd_disk->open_mutex);
put_device(&bdev->bd_device);
spin_lock(&blockdev_superblock->s_inode_list_lock);
}