From: Yu Kuai yukuai3@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I60QE9 CVE: NA
--------------------------------
Currently, the caller of bd_link_disk_holer() get 'bdev' by blkdev_get_by_dev(), which will look up 'bdev' by inode number 'dev'. Howerver, it's possible that del_gendisk() can be called currently, and 'bd_holder_dir' can be freed before bd_link_disk_holer() access it, thus use after free is triggered.
t1: t2: bdev = blkdev_get_by_dev del_gendisk kobject_put(bd_holder_dir) kobject_free() bd_link_disk_holder
Fix the problem by checking disk is still live and grabbing a reference to 'bd_holder_dir' first in bd_link_disk_holder().
Link: https://lore.kernel.org/all/20221103025541.1875809-3-yukuai1@huaweicloud.com... Signed-off-by: Yu Kuai yukuai3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- fs/block_dev.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/fs/block_dev.c b/fs/block_dev.c index 9868b21b8ef9..f2932e84055d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1305,16 +1305,31 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) struct bd_holder_disk *holder; int ret = 0;
- mutex_lock(&bdev->bd_mutex); + /* + * bdev could be deleted beneath us which would implicitly destroy + * the holder directory. Hold on to it. + */ + down_read(&bdev->bd_disk->lookup_sem); + if (!(disk->flags & GENHD_FL_UP)) { + up_read(&bdev->bd_disk->lookup_sem); + return -ENODEV; + } + + kobject_get(bdev->bd_part->holder_dir); + up_read(&bdev->bd_disk->lookup_sem);
+ mutex_lock(&bdev->bd_mutex); WARN_ON_ONCE(!bdev->bd_holder);
/* FIXME: remove the following once add_disk() handles errors */ - if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) + if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) { + kobject_put(bdev->bd_part->holder_dir); goto out_unlock; + }
holder = bd_find_holder_disk(bdev, disk); if (holder) { + kobject_put(bdev->bd_part->holder_dir); holder->refcnt++; goto out_unlock; } @@ -1336,11 +1351,6 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); if (ret) goto out_del; - /* - * bdev could be deleted beneath us which would implicitly destroy - * the holder directory. Hold on to it. - */ - kobject_get(bdev->bd_part->holder_dir);
list_add(&holder->list, &bdev->bd_holder_disks); goto out_unlock; @@ -1351,6 +1361,8 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) kfree(holder); out_unlock: mutex_unlock(&bdev->bd_mutex); + if (ret) + kobject_put(bdev->bd_part->holder_dir); return ret; } EXPORT_SYMBOL_GPL(bd_link_disk_holder);