In bdev_del_partition(), `part` is being looked up outside the critical section. This is causing bdev_del_partition() to delete the same partition more than once. Fix it by reverting commit cddae808aeb7. Fixes: cddae808aeb7 ("block: pass a hd_struct to delete_partition") Reported-and-tested-by: syzbot+6448f3c229bc52b82f69@xxxxxxxxxxxxxxxxxxxxxxxxx Cc: stable@xxxxxxxxxxxxxxx Cc: Hillf Danton <hdanton@xxxxxxxx> Link: https://syzkaller.appspot.com/bug?id=09fc5ec437ea150f28e8a19f5011c08ee73381af Signed-off-by: Peilin Ye <yepeilin.cs@xxxxxxxxx> --- block/blk.h | 2 +- block/genhd.c | 2 +- block/partitions/core.c | 22 ++++++++++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/block/blk.h b/block/blk.h index 49e2928a1632..f5a46d0020fb 100644 --- a/block/blk.h +++ b/block/blk.h @@ -350,7 +350,7 @@ char *disk_name(struct gendisk *hd, int partno, char *buf); #define ADDPART_FLAG_NONE 0 #define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_WHOLEDISK 2 -void delete_partition(struct gendisk *disk, struct hd_struct *part); +void delete_partition(struct gendisk *disk, int partno); int bdev_add_partition(struct block_device *bdev, int partno, sector_t start, sector_t length); int bdev_del_partition(struct block_device *bdev, int partno); diff --git a/block/genhd.c b/block/genhd.c index 99c64641c314..40abc70b92ab 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -913,7 +913,7 @@ void del_gendisk(struct gendisk *disk) DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); while ((part = disk_part_iter_next(&piter))) { invalidate_partition(disk, part->partno); - delete_partition(disk, part); + delete_partition(disk, part->partno); } disk_part_iter_exit(&piter); diff --git a/block/partitions/core.c b/block/partitions/core.c index e62a98a8eeb7..1fe1fe593423 100644 --- a/block/partitions/core.c +++ b/block/partitions/core.c @@ -310,17 +310,25 @@ int hd_ref_init(struct hd_struct *part) * Must be called either with bd_mutex held, before a disk can be opened or * after all disk users are gone. */ -void delete_partition(struct gendisk *disk, struct hd_struct *part) +void delete_partition(struct gendisk *disk, int partno) { struct disk_part_tbl *ptbl = rcu_dereference_protected(disk->part_tbl, 1); + struct hd_struct *part; + + if (partno >= ptbl->len) + return; + + part = rcu_dereference_protected(ptbl->part[partno], 1); + if (!part) + return; /* * ->part_tbl is referenced in this part's release handler, so * we have to hold the disk device */ get_device(disk_to_dev(part_to_disk(part))); - rcu_assign_pointer(ptbl->part[part->partno], NULL); + rcu_assign_pointer(ptbl->part[partno], NULL); kobject_put(part->holder_dir); device_del(part_to_dev(part)); @@ -531,10 +539,10 @@ int bdev_del_partition(struct block_device *bdev, int partno) if (!part) return -ENXIO; - ret = -ENOMEM; bdevp = bdget(part_devt(part)); + disk_put_part(part); if (!bdevp) - goto out_put_part; + return -ENOMEM; mutex_lock(&bdevp->bd_mutex); @@ -546,15 +554,13 @@ int bdev_del_partition(struct block_device *bdev, int partno) invalidate_bdev(bdevp); mutex_lock_nested(&bdev->bd_mutex, 1); - delete_partition(bdev->bd_disk, part); + delete_partition(bdev->bd_disk, partno); mutex_unlock(&bdev->bd_mutex); ret = 0; out_unlock: mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); -out_put_part: - disk_put_part(part); return ret; } @@ -627,7 +633,7 @@ int blk_drop_partitions(struct block_device *bdev) disk_part_iter_init(&piter, bdev->bd_disk, DISK_PITER_INCL_EMPTY); while ((part = disk_part_iter_next(&piter))) - delete_partition(bdev->bd_disk, part); + delete_partition(bdev->bd_disk, part->partno); disk_part_iter_exit(&piter); return 0; -- 2.25.1