diff options
author | Luo Meng <luomeng12@huawei.com> | 2022-02-21 10:02:44 +0800 |
---|---|---|
committer | Yang Yingliang <yangyingliang@huawei.com> | 2022-02-21 10:23:33 +0800 |
commit | 96786b6199ab5a34048593a2f3d3dbe9ac1a39ec (patch) | |
tree | 1bb134ce17fb01d2b1cd7cbd5489a0b812f76239 | |
parent | 212594e09b3c826da066456a4e33d7b9bda6a9b7 (diff) | |
download | openEuler-kernel-96786b6199ab5a34048593a2f3d3dbe9ac1a39ec.tar.gz |
dm-mpath: fix UAF in multipath_message()
hulk inclusion
category: bugfix
bugzilla: 186184, https://gitee.com/openeuler/kernel/issues/I4UH3B
CVE: NA
--------------------------------
If dm_get_device() create dd in multipath_message(),
and then call table_deps() after dm_put_table_device(),
it will lead to concurrency UAF bugs.
One of the concurrency UAF can be shown as below:
(USE) | (FREE)
| target_message
| multipath_message
| dm_put_device
| dm_put_table_device #
| kfree(td) # table_device *td
ioctl # DM_TABLE_DEPS_CMD | ...
table_deps | ...
dm_get_live_or_inactive_table | ...
retrieve_dep | ...
list_for_each_entry | ...
deps->dev[count++] = | ...
huge_encode_dev | ...
(dd->dm_dev->bdev->bd_dev) | list_del(&dd->list)
| kfree(dd) # dm_dev_internal
The root cause of UAF bugs is that find_device() failed in
dm_get_device() and will create dd and refcount set 1, kfree()
in dm_put_table() is not protected. When td, which there are
still pointers point to, is released, the concurrency UAF bug
will happen.
This patch add a flag to determine whether to create a new dd.
Signed-off-by: Luo Meng <luomeng12@huawei.com>
Reviewed-by: Jason Yan <yanaijie@huawei.com>
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
-rw-r--r-- | drivers/md/dm-mpath.c | 2 | ||||
-rw-r--r-- | drivers/md/dm-table.c | 43 | ||||
-rw-r--r-- | include/linux/device-mapper.h | 2 |
3 files changed, 29 insertions, 18 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 207ca0ad0b59df..6c43c09689c7f8 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1844,7 +1844,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv, goto out; } - r = dm_get_device(ti, argv[1], dm_table_get_mode(ti->table), &dev); + r = __dm_get_device(ti, argv[1], dm_table_get_mode(ti->table), &dev, false); if (r) { DMWARN("message: error getting device %s", argv[1]); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 16acc33858dd2f..b00f767af0e3a4 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -422,12 +422,8 @@ dev_t dm_get_dev_t(const char *path) } EXPORT_SYMBOL_GPL(dm_get_dev_t); -/* - * Add a device to the list, or just increment the usage count if - * it's already present. - */ -int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, - struct dm_dev **result) +int __dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, + struct dm_dev **result, bool create_dd) { int r; dev_t dev; @@ -451,19 +447,21 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, dd = find_device(&t->devices, dev); if (!dd) { - dd = kmalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - return -ENOMEM; - - if ((r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev))) { - kfree(dd); - return r; - } + if (create_dd) { + dd = kmalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; - refcount_set(&dd->count, 1); - list_add(&dd->list, &t->devices); - goto out; + if ((r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev))) { + kfree(dd); + return r; + } + refcount_set(&dd->count, 1); + list_add(&dd->list, &t->devices); + goto out; + } else + return -ENODEV; } else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) { r = upgrade_mode(dd, mode, t->md); if (r) @@ -474,6 +472,17 @@ out: *result = dd->dm_dev; return 0; } +EXPORT_SYMBOL(__dm_get_device); + +/* + * Add a device to the list, or just increment the usage count if + * it's already present. + */ +int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, + struct dm_dev **result) +{ + return __dm_get_device(ti, path, mode, result, true); +} EXPORT_SYMBOL(dm_get_device); static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index cde6708644aed5..ffbc06a9c342d4 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -156,6 +156,8 @@ dev_t dm_get_dev_t(const char *path); int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, struct dm_dev **result); void dm_put_device(struct dm_target *ti, struct dm_dev *d); +int __dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, + struct dm_dev **result, bool create_dd); /* * Information about a target type |