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: 1643dfa4c2
("rbd: introduce a per-device ordered workqueue")
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
0f2fd21b5b
commit
ae16346078
@@ -5369,8 +5369,7 @@ static void rbd_dev_release(struct device *dev)
|
|||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
|
static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec)
|
||||||
struct rbd_spec *spec)
|
|
||||||
{
|
{
|
||||||
struct rbd_device *rbd_dev;
|
struct rbd_device *rbd_dev;
|
||||||
|
|
||||||
@@ -5415,9 +5414,6 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
|
|||||||
rbd_dev->dev.parent = &rbd_root_dev;
|
rbd_dev->dev.parent = &rbd_root_dev;
|
||||||
device_initialize(&rbd_dev->dev);
|
device_initialize(&rbd_dev->dev);
|
||||||
|
|
||||||
rbd_dev->rbd_client = rbdc;
|
|
||||||
rbd_dev->spec = spec;
|
|
||||||
|
|
||||||
return rbd_dev;
|
return rbd_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5430,12 +5426,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
|
|||||||
{
|
{
|
||||||
struct rbd_device *rbd_dev;
|
struct rbd_device *rbd_dev;
|
||||||
|
|
||||||
rbd_dev = __rbd_dev_create(rbdc, spec);
|
rbd_dev = __rbd_dev_create(spec);
|
||||||
if (!rbd_dev)
|
if (!rbd_dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
rbd_dev->opts = opts;
|
|
||||||
|
|
||||||
/* get an id and fill in device name */
|
/* get an id and fill in device name */
|
||||||
rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0,
|
rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0,
|
||||||
minor_to_rbd_dev_id(1 << MINORBITS),
|
minor_to_rbd_dev_id(1 << MINORBITS),
|
||||||
@@ -5452,6 +5446,10 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
|
|||||||
/* we have a ref from do_rbd_add() */
|
/* we have a ref from do_rbd_add() */
|
||||||
__module_get(THIS_MODULE);
|
__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);
|
dout("%s rbd_dev %p dev_id %d\n", __func__, rbd_dev, rbd_dev->dev_id);
|
||||||
return rbd_dev;
|
return rbd_dev;
|
||||||
|
|
||||||
@@ -6812,7 +6810,7 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
|
|||||||
goto out_err;
|
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) {
|
if (!parent) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
@@ -6822,8 +6820,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
|
|||||||
* Images related by parent/child relationships always share
|
* Images related by parent/child relationships always share
|
||||||
* rbd_client and spec/parent_spec, so bump their refcounts.
|
* rbd_client and spec/parent_spec, so bump their refcounts.
|
||||||
*/
|
*/
|
||||||
__rbd_get_client(rbd_dev->rbd_client);
|
parent->rbd_client = __rbd_get_client(rbd_dev->rbd_client);
|
||||||
rbd_spec_get(rbd_dev->parent_spec);
|
parent->spec = rbd_spec_get(rbd_dev->parent_spec);
|
||||||
|
|
||||||
__set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
|
__set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user