block: fix locking in bdev_del_partition
authorChristoph Hellwig <hch@lst.de>
Tue, 1 Sep 2020 09:59:41 +0000 (11:59 +0200)
committerJens Axboe <axboe@kernel.dk>
Tue, 1 Sep 2020 14:35:35 +0000 (08:35 -0600)
We need to hold the whole device bd_mutex to protect against
other thread concurrently deleting out partition before we get
to it, and thus causing a use after free.

Fixes: cddae808aeb7 ("block: pass a hd_struct to delete_partition")
Reported-by: syzbot+6448f3c229bc52b82f69@syzkaller.appspotmail.com
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/partitions/core.c

index 5a18c8e..5b4869c 100644 (file)
@@ -532,19 +532,20 @@ int bdev_add_partition(struct block_device *bdev, int partno,
 int bdev_del_partition(struct block_device *bdev, int partno)
 {
        struct block_device *bdevp;
-       struct hd_struct *part;
-       int ret = 0;
-
-       part = disk_get_part(bdev->bd_disk, partno);
-       if (!part)
-               return -ENXIO;
+       struct hd_struct *part = NULL;
+       int ret;
 
-       ret = -ENOMEM;
-       bdevp = bdget(part_devt(part));
+       bdevp = bdget_disk(bdev->bd_disk, partno);
        if (!bdevp)
-               goto out_put_part;
+               return -ENOMEM;
 
        mutex_lock(&bdevp->bd_mutex);
+       mutex_lock_nested(&bdev->bd_mutex, 1);
+
+       ret = -ENXIO;
+       part = disk_get_part(bdev->bd_disk, partno);
+       if (!part)
+               goto out_unlock;
 
        ret = -EBUSY;
        if (bdevp->bd_openers)
@@ -553,16 +554,14 @@ int bdev_del_partition(struct block_device *bdev, int partno)
        sync_blockdev(bdevp);
        invalidate_bdev(bdevp);
 
-       mutex_lock_nested(&bdev->bd_mutex, 1);
        delete_partition(bdev->bd_disk, part);
-       mutex_unlock(&bdev->bd_mutex);
-
        ret = 0;
 out_unlock:
+       mutex_unlock(&bdev->bd_mutex);
        mutex_unlock(&bdevp->bd_mutex);
        bdput(bdevp);
-out_put_part:
-       disk_put_part(part);
+       if (part)
+               disk_put_part(part);
        return ret;
 }