提交 c5012bd4 编写于 作者: L Luo Meng 提交者: Zheng Zengkai

dm-mpath: fix UAF in multipath_message()

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I5KK52
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: NLuo Meng <luomeng12@huawei.com>
Reviewed-by: NJason Yan <yanaijie@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 749b58dc
...@@ -1962,7 +1962,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv, ...@@ -1962,7 +1962,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv,
goto out; 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) { if (r) {
DMWARN("message: error getting device %s", DMWARN("message: error getting device %s",
argv[1]); argv[1]);
......
...@@ -361,12 +361,8 @@ dev_t dm_get_dev_t(const char *path) ...@@ -361,12 +361,8 @@ dev_t dm_get_dev_t(const char *path)
} }
EXPORT_SYMBOL_GPL(dm_get_dev_t); EXPORT_SYMBOL_GPL(dm_get_dev_t);
/* int __dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
* Add a device to the list, or just increment the usage count if struct dm_dev **result, bool create_dd)
* it's already present.
*/
int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
struct dm_dev **result)
{ {
int r; int r;
dev_t dev; dev_t dev;
...@@ -390,19 +386,22 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, ...@@ -390,19 +386,22 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
dd = find_device(&t->devices, dev); dd = find_device(&t->devices, dev);
if (!dd) { if (!dd) {
dd = kmalloc(sizeof(*dd), GFP_KERNEL); if (create_dd) {
if (!dd) dd = kmalloc(sizeof(*dd), GFP_KERNEL);
return -ENOMEM; if (!dd)
return -ENOMEM;
if ((r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev))) {
kfree(dd);
return r;
}
refcount_set(&dd->count, 1); r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev);
list_add(&dd->list, &t->devices); if (r) {
goto out; 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)) { } else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) {
r = upgrade_mode(dd, mode, t->md); r = upgrade_mode(dd, mode, t->md);
if (r) if (r)
...@@ -413,6 +412,17 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, ...@@ -413,6 +412,17 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
*result = dd->dm_dev; *result = dd->dm_dev;
return 0; 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); EXPORT_SYMBOL(dm_get_device);
static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
......
...@@ -162,6 +162,8 @@ dev_t dm_get_dev_t(const char *path); ...@@ -162,6 +162,8 @@ dev_t dm_get_dev_t(const char *path);
int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
struct dm_dev **result); struct dm_dev **result);
void dm_put_device(struct dm_target *ti, struct dm_dev *d); 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 * Information about a target type
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册