Skip to content

Commit f7c4d9b

Browse files
committed
rbd: avoid use-after-free in do_rbd_add() when rbd_dev_create() fails
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 <[email protected]>. Cc: [email protected] Fixes: 1643dfa ("rbd: introduce a per-device ordered workqueue") Signed-off-by: Ilya Dryomov <[email protected]>
1 parent e027253 commit f7c4d9b

File tree

1 file changed

+9
-11
lines changed

1 file changed

+9
-11
lines changed

drivers/block/rbd.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5292,8 +5292,7 @@ static void rbd_dev_release(struct device *dev)
52925292
module_put(THIS_MODULE);
52935293
}
52945294

5295-
static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
5296-
struct rbd_spec *spec)
5295+
static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec)
52975296
{
52985297
struct rbd_device *rbd_dev;
52995298

@@ -5338,9 +5337,6 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
53385337
rbd_dev->dev.parent = &rbd_root_dev;
53395338
device_initialize(&rbd_dev->dev);
53405339

5341-
rbd_dev->rbd_client = rbdc;
5342-
rbd_dev->spec = spec;
5343-
53445340
return rbd_dev;
53455341
}
53465342

@@ -5353,12 +5349,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
53535349
{
53545350
struct rbd_device *rbd_dev;
53555351

5356-
rbd_dev = __rbd_dev_create(rbdc, spec);
5352+
rbd_dev = __rbd_dev_create(spec);
53575353
if (!rbd_dev)
53585354
return NULL;
53595355

5360-
rbd_dev->opts = opts;
5361-
53625356
/* get an id and fill in device name */
53635357
rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0,
53645358
minor_to_rbd_dev_id(1 << MINORBITS),
@@ -5375,6 +5369,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
53755369
/* we have a ref from do_rbd_add() */
53765370
__module_get(THIS_MODULE);
53775371

5372+
rbd_dev->rbd_client = rbdc;
5373+
rbd_dev->spec = spec;
5374+
rbd_dev->opts = opts;
5375+
53785376
dout("%s rbd_dev %p dev_id %d\n", __func__, rbd_dev, rbd_dev->dev_id);
53795377
return rbd_dev;
53805378

@@ -6736,7 +6734,7 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
67366734
goto out_err;
67376735
}
67386736

6739-
parent = __rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec);
6737+
parent = __rbd_dev_create(rbd_dev->parent_spec);
67406738
if (!parent) {
67416739
ret = -ENOMEM;
67426740
goto out_err;
@@ -6746,8 +6744,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
67466744
* Images related by parent/child relationships always share
67476745
* rbd_client and spec/parent_spec, so bump their refcounts.
67486746
*/
6749-
__rbd_get_client(rbd_dev->rbd_client);
6750-
rbd_spec_get(rbd_dev->parent_spec);
6747+
parent->rbd_client = __rbd_get_client(rbd_dev->rbd_client);
6748+
parent->spec = rbd_spec_get(rbd_dev->parent_spec);
67516749

67526750
__set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
67536751

0 commit comments

Comments
 (0)