[PATCH 3/3] nvme-core: preventing double freeing in ctrl release
Keith Busch
kbusch at kernel.org
Tue Apr 25 14:45:04 PDT 2023
On Tue, Apr 25, 2023 at 06:18:36PM -0300, Irvin Cote wrote:
> The teardown logic and the release method for
> the controller both free the same resources.
> This introduces double-freeing issues.
> Since the release of the ctrl is the last step
> of the teardown, we can solve this by setting the freed
> pointers to NULL in the teardown path and add checks
> in the release method.
> The only issue is with ida_free as ida doesn't offer any way to
> indicate that an ID has already been freed.
> In our specific case we can set the ID to -1 to indicate that.
> We can do that because, the way we've setup ida to manage ctrl instances,
> it always returns non-negative IDS.
Your line wrap setting is way too short. 72 characters is recommended.
> @@ -5091,14 +5091,15 @@ static void nvme_free_ctrl(struct device *dev)
> container_of(dev, struct nvme_ctrl, ctrl_device);
> struct nvme_subsystem *subsys = ctrl->subsys;
>
> - if (!subsys || ctrl->instance != subsys->instance)
> + if ((!subsys || ctrl->instance != subsys->instance) && ctrl->instance != -1)
> ida_free(&nvme_instance_ida, ctrl->instance);
>
> nvme_free_cels(ctrl);
> nvme_mpath_uninit(ctrl);
> nvme_auth_stop(ctrl);
> nvme_auth_free(ctrl);
> - __free_page(ctrl->discard_page);
> + if(ctrl->discard_page)
> + __free_page(ctrl->discard_page);
> free_opal_dev(ctrl->opal_dev);
>
> if (subsys) {
> @@ -5202,8 +5203,10 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
> kfree_const(ctrl->device->kobj.name);
> out_release_instance:
> ida_free(&nvme_instance_ida, ctrl->instance);
> + ctrl->instance = -1;
Isn't the problem that the nvme_put_ctrl() in this function's 'goto' error is
doubling the rest of the unwinding, which means your ida_free() is happening
twice?
I think instead the nvme_get_ctrl() should be the last thing done in the
init_ctrl() function, then the rest of the cleanup on error would be simpler.
> out_free_discard_page:
> __free_page(ctrl->discard_page);
> + ctrl->discard_page = NULL;
> return ret;
> }
> EXPORT_SYMBOL_GPL(nvme_init_ctrl);
> diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
> index 65e4b9f1b632..ff5c876739bc 100644
> --- a/drivers/nvme/host/pci.c
> +++ b/drivers/nvme/host/pci.c
> @@ -2681,8 +2681,10 @@ static void nvme_pci_free_ctrl(struct nvme_ctrl *ctrl)
>
> nvme_free_tagset(dev);
> put_device(dev->dev);
> - kfree(dev->queues);
> - kfree(dev);
> + if(dev->queues)
> + kfree(dev->queues);
> + if(dev)
> + kfree(dev);
kfree() already checks for NULL, so these are redundant.
> }
>
> static void nvme_reset_work(struct work_struct *work)
> @@ -2973,8 +2975,10 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,
> out_put_device:
> put_device(dev->dev);
> kfree(dev->queues);
> + dev->queues = NULL;
> out_free_dev:
> kfree(dev);
> + dev = NULL;
> nvme_put_ctrl(&dev->ctrl);
> return ERR_PTR(ret);
> }
> --
More information about the Linux-nvme
mailing list