From: Mike Snitzer <snitzer@kernel.org>
To: "lilingfeng (A)" <lilingfeng3@huawei.com>
Cc: ejt@redhat.com, Luo Meng <luomeng@huaweicloud.com>,
dm-devel@redhat.com, mpatocka@redhat.com, luomeng12@huawei.com,
yukuai3@huawei.com, agk@redhat.com
Subject: Re: [dm-devel] [dm-devel resend] dm mpath: fix UAF in multipath_message()
Date: Wed, 2 Aug 2023 12:33:24 -0400 [thread overview]
Message-ID: <ZMqFVGX/ZbVABDJ6@redhat.com> (raw)
In-Reply-To: <eb9e1d80-c62c-c62f-4fc2-5be21fc75472@huawei.com>
On Thu, May 18 2023 at 8:11P -0400,
lilingfeng (A) <lilingfeng3@huawei.com> wrote:
>
> 在 2022/10/18 3:56, Mike Snitzer 写道:
> > On Mon, Oct 10 2022 at 10:41P -0400,
> > Luo Meng <luomeng@huaweicloud.com> wrote:
> >
> > > From: Luo Meng <luomeng12@huawei.com>
> > >
> > > 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.
> > >
> > > Fixes: 8215d6ec5fee(dm table: remove unused dm_get_device range parameters)
> > > Signed-off-by: Luo Meng <luomeng12@huawei.com>
> > > ---
> > > drivers/md/dm-mpath.c | 2 +-
> > > drivers/md/dm-table.c | 43 +++++++++++++++++++++--------------
> > > 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 0e325469a252..1f51059520ac 100644
> > > --- a/drivers/md/dm-mpath.c
> > > +++ b/drivers/md/dm-mpath.c
> > > @@ -2001,7 +2001,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 d8034ff0cb24..ad18a41f1608 100644
> > > --- a/drivers/md/dm-table.c
> > > +++ b/drivers/md/dm-table.c
> > > @@ -338,12 +338,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;
> > > @@ -367,19 +363,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)
> > > @@ -390,6 +388,17 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
> > > *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 04c6acf7faaa..ef4031a844b6 100644
> > > --- a/include/linux/device-mapper.h
> > > +++ b/include/linux/device-mapper.h
> > > @@ -178,6 +178,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
> > > --
> > > 2.31.1
> > >
> > This patch is treating the one multipath case like it is only consumer
> > of dm_get_device() that might have this issue. It feels too focused on
> > an instance where dm_get_device()'s side-effect of creating the device
> > is unwelcome. Feels like you're treating the symptom rather than the
> > problem (e.g. that the "kfree() in dm_put_table() is not protected"?)
> >
> > Mike
>
> In other cases, kfree() in dm_put_table() is protected by srcu.
> For example:
> USE FREE
> table_deps dev_remove
> dm_get_live_or_inactive_table dm_sync_table
> // lock
> srcu_read_lock(&md->io_barrier)
> // wait for unlock
> synchronize_srcu(&md->io_barrier)
> retrieve_deps
> dm_put_live_table
> // unlock
> srcu_read_unlock(&md->io_barrier...)
> dm_table_destroy
> linear_dtr
> dm_put_device
> dm_put_table_device
>
> However, in multipath_message(), the dm_dev is created and destroyed
> under srcu_read_lock, which means destroying dm_dev in this process
> and using dm_dev in other process will happen at same time since both
> of them hold srcu_read_lock.
>
> target_message
> dm_get_live_table // lock
> multipath_message
> dm_get_device // created
> // may be got by other processes
> dm_put_device // destroyed
> // may be used by other processes
> dm_put_live_table // unlock
>
> We figured out some other solutions:
> 1) Judge refcount of dd under some lock before using dm_dev;
> 2) Get dd from list and use dm_dev under rcu;
> 3) Implement dm_get_device_xxx() with reference to dm_get_device()
> for dm-mpath to avoid creating dd when find_device() failed.
>
> Compared to solutions above, Luo Meng's patch may be more appropriate.
> Any suggestions will be appreciated.
>
> Li
[Cc'ing Mikulas so he can take a look at this too.]
The proposed patch papers over the issue, leaving a landmine for some
future DM target.
In addition, the patch header is very muddled about relation between
table_device and dm_dev_internal. And also needs clarification like:
"kfree(td) in dm_put_table_device() is not interlocked with
dm_dev_internal list (table->devices) management"
Also, the commit referenced with the "Fixes:" is bogus.
Wouldn't this change address the UAF (albeit, list_for_each_entry_safe
likely needed in methods like retrieve_deps())?
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 7d208b2b1a19..39e4c9ee8f16 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -434,9 +434,9 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d)
return;
}
if (refcount_dec_and_test(&dd->count)) {
- dm_put_table_device(ti->table->md, d);
list_del(&dd->list);
kfree(dd);
+ dm_put_table_device(ti->table->md, d);
}
}
EXPORT_SYMBOL(dm_put_device);
--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel
next prev parent reply other threads:[~2023-08-02 16:33 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-10 14:41 [dm-devel] [dm-devel resend] dm mpath: fix UAF in multipath_message() Luo Meng
2022-10-17 19:56 ` Mike Snitzer
2022-11-29 9:03 ` Luo Meng
2023-05-18 12:11 ` lilingfeng (A)
2023-08-02 3:06 ` Li Lingfeng
2023-08-02 16:33 ` Mike Snitzer [this message]
2023-08-03 11:27 ` Li Lingfeng
2023-08-08 19:06 ` Mikulas Patocka
2023-08-09 10:44 ` Mikulas Patocka
2023-09-06 2:16 ` Li Lingfeng
2023-09-06 3:15 ` Mike Snitzer
2023-09-06 3:46 ` Li Lingfeng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZMqFVGX/ZbVABDJ6@redhat.com \
--to=snitzer@kernel.org \
--cc=agk@redhat.com \
--cc=dm-devel@redhat.com \
--cc=ejt@redhat.com \
--cc=lilingfeng3@huawei.com \
--cc=luomeng12@huawei.com \
--cc=luomeng@huaweicloud.com \
--cc=mpatocka@redhat.com \
--cc=yukuai3@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.