summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2018-02-26 07:01:40 -0500
committerJens Axboe <axboe@kernel.dk>2018-02-26 11:48:42 -0500
commit897366537fb65e87755b822360c230354c3fc73b (patch)
tree6e5106b9b1d9aacdd0e12e560a8cc8021d4cb1e6 /fs
parent9df6c29912315186fef1c79cc15b758ace84175b (diff)
genhd: Fix use after free in __blkdev_get()
When two blkdev_open() calls race with device removal and recreation, __blkdev_get() can use looked up gendisk after it is freed: CPU0 CPU1 CPU2 del_gendisk(disk); bdev_unhash_inode(inode); blkdev_open() blkdev_open() bdev = bd_acquire(inode); - creates and returns new inode bdev = bd_acquire(inode); - returns the same inode __blkdev_get(devt) __blkdev_get(devt) disk = get_gendisk(devt); - got structure of device going away <finish device removal> <new device gets created under the same device number> disk = get_gendisk(devt); - got new device structure if (!bdev->bd_openers) { does the first open } if (!bdev->bd_openers) - false } else { put_disk_and_module(disk) - remember this was old device - this was last ref and disk is now freed } disk_unblock_events(disk); -> oops Fix the problem by making sure we drop reference to disk in __blkdev_get() only after we are really done with it. Reported-by: Hou Tao <houtao1@huawei.com> Tested-by: Hou Tao <houtao1@huawei.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r--fs/block_dev.c7
1 files changed, 5 insertions, 2 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1dbbf847911a..fe41a76769fa 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1409,6 +1409,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
1409 int ret; 1409 int ret;
1410 int partno; 1410 int partno;
1411 int perm = 0; 1411 int perm = 0;
1412 bool first_open = false;
1412 1413
1413 if (mode & FMODE_READ) 1414 if (mode & FMODE_READ)
1414 perm |= MAY_READ; 1415 perm |= MAY_READ;
@@ -1435,6 +1436,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
1435 disk_block_events(disk); 1436 disk_block_events(disk);
1436 mutex_lock_nested(&bdev->bd_mutex, for_part); 1437 mutex_lock_nested(&bdev->bd_mutex, for_part);
1437 if (!bdev->bd_openers) { 1438 if (!bdev->bd_openers) {
1439 first_open = true;
1438 bdev->bd_disk = disk; 1440 bdev->bd_disk = disk;
1439 bdev->bd_queue = disk->queue; 1441 bdev->bd_queue = disk->queue;
1440 bdev->bd_contains = bdev; 1442 bdev->bd_contains = bdev;
@@ -1520,14 +1522,15 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
1520 if (ret) 1522 if (ret)
1521 goto out_unlock_bdev; 1523 goto out_unlock_bdev;
1522 } 1524 }
1523 /* only one opener holds refs to the module and disk */
1524 put_disk_and_module(disk);
1525 } 1525 }
1526 bdev->bd_openers++; 1526 bdev->bd_openers++;
1527 if (for_part) 1527 if (for_part)
1528 bdev->bd_part_count++; 1528 bdev->bd_part_count++;
1529 mutex_unlock(&bdev->bd_mutex); 1529 mutex_unlock(&bdev->bd_mutex);
1530 disk_unblock_events(disk); 1530 disk_unblock_events(disk);
1531 /* only one opener holds refs to the module and disk */
1532 if (!first_open)
1533 put_disk_and_module(disk);
1531 return 0; 1534 return 0;
1532 1535
1533 out_clear: 1536 out_clear: