aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuo Meng <luomeng12@huawei.com>2022-02-21 10:02:44 +0800
committerYang Yingliang <yangyingliang@huawei.com>2022-02-21 10:23:33 +0800
commit96786b6199ab5a34048593a2f3d3dbe9ac1a39ec (patch)
tree1bb134ce17fb01d2b1cd7cbd5489a0b812f76239
parent212594e09b3c826da066456a4e33d7b9bda6a9b7 (diff)
downloadopenEuler-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.c2
-rw-r--r--drivers/md/dm-table.c43
-rw-r--r--include/linux/device-mapper.h2
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