Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9787b328 authored by Ilya Dryomov's avatar Ilya Dryomov Committed by Greg Kroah-Hartman
Browse files

rbd: avoid use-after-free in do_rbd_add() when rbd_dev_create() fails



commit f7c4d9b133c7a04ca619355574e96b6abf209fba upstream.

If getting an ID or setting up a work queue in rbd_dev_create() fails,
use-after-free on rbd_dev->rbd_client, rbd_dev->spec and rbd_dev->opts
is triggered in do_rbd_add().  The root cause is that the ownership of
these structures is transfered to rbd_dev prematurely and they all end
up getting freed when rbd_dev_create() calls rbd_dev_free() prior to
returning to do_rbd_add().

Found by Linux Verification Center (linuxtesting.org) with SVACE, an
incomplete patch submitted by Natalia Petrova <n.petrova@fintech.ru>.

Cc: stable@vger.kernel.org
Fixes: 1643dfa4 ("rbd: introduce a per-device ordered workqueue")
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7055754d
Loading
Loading
Loading
Loading
+9 −11
Original line number Diff line number Diff line
@@ -5529,8 +5529,7 @@ static void rbd_dev_release(struct device *dev)
		module_put(THIS_MODULE);
}

static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
					   struct rbd_spec *spec)
static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec)
{
	struct rbd_device *rbd_dev;

@@ -5575,9 +5574,6 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
	rbd_dev->dev.parent = &rbd_root_dev;
	device_initialize(&rbd_dev->dev);

	rbd_dev->rbd_client = rbdc;
	rbd_dev->spec = spec;

	return rbd_dev;
}

@@ -5590,12 +5586,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
{
	struct rbd_device *rbd_dev;

	rbd_dev = __rbd_dev_create(rbdc, spec);
	rbd_dev = __rbd_dev_create(spec);
	if (!rbd_dev)
		return NULL;

	rbd_dev->opts = opts;

	/* get an id and fill in device name */
	rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0,
					 minor_to_rbd_dev_id(1 << MINORBITS),
@@ -5612,6 +5606,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
	/* we have a ref from do_rbd_add() */
	__module_get(THIS_MODULE);

	rbd_dev->rbd_client = rbdc;
	rbd_dev->spec = spec;
	rbd_dev->opts = opts;

	dout("%s rbd_dev %p dev_id %d\n", __func__, rbd_dev, rbd_dev->dev_id);
	return rbd_dev;

@@ -6827,7 +6825,7 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
		goto out_err;
	}

	parent = __rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec);
	parent = __rbd_dev_create(rbd_dev->parent_spec);
	if (!parent) {
		ret = -ENOMEM;
		goto out_err;
@@ -6837,8 +6835,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
	 * Images related by parent/child relationships always share
	 * rbd_client and spec/parent_spec, so bump their refcounts.
	 */
	__rbd_get_client(rbd_dev->rbd_client);
	rbd_spec_get(rbd_dev->parent_spec);
	parent->rbd_client = __rbd_get_client(rbd_dev->rbd_client);
	parent->spec = rbd_spec_get(rbd_dev->parent_spec);

	ret = rbd_dev_image_probe(parent, depth);
	if (ret < 0)