Commit 60050273 authored by Li Nan's avatar Li Nan
Browse files

loop: use lo->lo_disk for kobject_uevent

hulk inclusion
category: bugfix
bugzilla: 189922, https://gitee.com/openeuler/kernel/issues/I9PXKA



--------------------------------

The current implimentation of partitions for loop has several issues.
'loop_device' is at the gendisk level, but it uses 'lo_device' to store
block_device. A gendisk can have multiple block_devices, and 'lo_device'
can be set to anyone of them through loop_configure().

This usage can lead to a null-ptr-deref issue. If 'lo_device' is set to
a block_device of partition, this partition is closed and 'bd_openers' is
dec to 0, the 'bd_disk' of that block_device will be set to NULL in
__blkdev_put(). In this case, Accessing 'lo_device->bd_disk' will trigger
the issue. The problem can be reproduced as follows:
  1. create loop device loop0 and create a partition loop0p1.
  2. submit the ioctl LOOP_CLR_FD by loop0.
  3. submit the ioctl LOOP_SET_FD by loop0p1.
  4. submit ioctl such as LOOP_SET_STATUS by loop0.

Fix it by using 'lo->lo_disk' instead of 'lo->lo_device->bd_disk' for
kobject_uevent().

Fixes: c3473c63 ("generate "change" uevent for loop device")
Signed-off-by: default avatarLi Nan <linan122@huawei.com>
parent aec28351
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -241,7 +241,7 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
	set_capacity(lo->lo_disk, x);
	bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9);
	/* let user-space know about the new size */
	kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
	kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
	return 0;
}

@@ -1002,7 +1002,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
	bd_set_size(bdev, size << 9);
	loop_sysfs_init(lo);
	/* let user-space know about the new size */
	kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
	kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);

	set_blocksize(bdev, S_ISBLK(inode->i_mode) ?
		      block_size(inode->i_bdev) : PAGE_SIZE);
@@ -1123,7 +1123,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
	if (bdev) {
		bd_set_size(bdev, 0);
		/* let user-space know about this change */
		kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
		kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
	}
	mapping_set_gfp_mask(filp->f_mapping, gfp);
	/* This is safe: open() is still holding a reference. */