If the rbd_dev_create() fails after assignment 'opts' to 'rbd_dev->opts', double free of 'rbd_options' happens: one is in rbd_dev_free() and another one is in do_rbd_add(). If the rbd_dev_create() fails, for 'spec' it will be freed in rbd_dev_create()->rbd_spec_put() first and then in do_rbd_add() it will call rbd_spec_put() again. The same for 'rbd_client'. Unlike 'rbd_dev->opts', 'rbd_dev->spec' and 'rbd_dev->rbd_client' are ref-counted, that's why the ref-count underflow warning should be generated in rbd_spec_put() and rbd_put_client() to handle the return values of kref_put(). Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 1643dfa4c2c8 ("rbd: introduce a per-device ordered workqueue") Signed-off-by: Natalia Petrova <n.petrova@xxxxxxxxxx> Signed-off-by: Alexey Khoroshilov <khoroshilov@xxxxxxxxx> Signed-off-by: Nikita Zhandarovich <n.zhandarovich@xxxxxxxxxx> --- v2: Remarks on the processing of 'rbd_dev->spec' and 'rbd_dev->rbd_client' by Ilya Dryomov <idryomov@xxxxxxxxx> and Xiubo Li <xiubli@xxxxxxxxxx> were taken into account. drivers/block/rbd.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 04453f4a319c..f3f253febe0f 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -889,8 +889,10 @@ static void rbd_client_release(struct kref *kref) */ static void rbd_put_client(struct rbd_client *rbdc) { - if (rbdc) - kref_put(&rbdc->kref, rbd_client_release); + if (rbdc) { + if (!kref_put(&rbdc->kref, rbd_client_release)) + pr_warn("The reference count underflow\n"); + } } /* @@ -5225,8 +5227,10 @@ static struct rbd_spec *rbd_spec_get(struct rbd_spec *spec) static void rbd_spec_free(struct kref *kref); static void rbd_spec_put(struct rbd_spec *spec) { - if (spec) - kref_put(&spec->kref, rbd_spec_free); + if (spec) { + if (!kref_put(&spec->kref, rbd_spec_free)) + pr_warn("The reference count underflow\n"); + } } static struct rbd_spec *rbd_spec_alloc(void) @@ -5357,7 +5361,6 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, 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, @@ -5372,6 +5375,7 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, if (!rbd_dev->task_wq) goto fail_dev_id; + rbd_dev->opts = opts; /* we have a ref from do_rbd_add() */ __module_get(THIS_MODULE); -- 2.34.1