public inbox for linux-nvme@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Adjusting deref count in nvme_init_ctrl
@ 2023-04-25 21:18 Irvin Cote
  2023-04-25 21:18 ` [PATCH 1/3] nvme-core: nvme_init_ctrl cleanup Irvin Cote
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Irvin Cote @ 2023-04-25 21:18 UTC (permalink / raw)
  To: hch; +Cc: kbusch, axboe, sagi, linux-nvme, chaitanyak, Irvin Cote

The following three patches aim to solve the following issues:

-In case the teardown path of nvme_init_ctrl is triggered the driver
 exits with a non-zero ref count on the controller. 

-Teardown paths and the ctrl's release method perform similar tasks
 leading to double-freeing issues.

In details:

-The first patch is just a ceanup of nvme_init_ctrl teardown logic.

-The second patch adds an nvme_put_ctrl at the end of nvme_init_ctrl's
teardown path to ensure the controller releases all references upon exit.

-The third one addresses the double-freeing issues 

Irvin Cote (3):
  nvme-core: nvme_init_ctrl cleanup
  nvme-pci: Adjusting ctrl deref count
  nvme-core: preventing double freeing in ctrl release

 drivers/nvme/host/core.c | 20 ++++++++++----------
 drivers/nvme/host/pci.c  |  9 +++++++--
 2 files changed, 17 insertions(+), 12 deletions(-)

-- 
2.39.2



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] nvme-core: nvme_init_ctrl cleanup
  2023-04-25 21:18 [PATCH 0/3] Adjusting deref count in nvme_init_ctrl Irvin Cote
@ 2023-04-25 21:18 ` Irvin Cote
  2023-04-25 21:18 ` [PATCH 2/3] nvme-pci: Adjusting ctrl deref count Irvin Cote
  2023-04-25 21:18 ` [PATCH 3/3] nvme-core: preventing double freeing in ctrl release Irvin Cote
  2 siblings, 0 replies; 6+ messages in thread
From: Irvin Cote @ 2023-04-25 21:18 UTC (permalink / raw)
  To: hch; +Cc: kbusch, axboe, sagi, linux-nvme, chaitanyak, Irvin Cote

Removed an incoherent check at the last exit label
and renamed it for better clarity.

Signed-off-by: Irvin Cote <irvincoteg@gmail.com>
---
 drivers/nvme/host/core.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index d6a9bac91a4c..353443250d48 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -5149,14 +5149,12 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 	BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) >
 			PAGE_SIZE);
 	ctrl->discard_page = alloc_page(GFP_KERNEL);
-	if (!ctrl->discard_page) {
-		ret = -ENOMEM;
-		goto out;
-	}
+	if (!ctrl->discard_page)
+		return -ENOMEM;
 
 	ret = ida_alloc(&nvme_instance_ida, GFP_KERNEL);
 	if (ret < 0)
-		goto out;
+		goto out_free_discard_page;
 	ctrl->instance = ret;
 
 	device_initialize(&ctrl->ctrl_device);
@@ -5204,9 +5202,8 @@ 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);
-out:
-	if (ctrl->discard_page)
-		__free_page(ctrl->discard_page);
+out_free_discard_page:
+	__free_page(ctrl->discard_page);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nvme_init_ctrl);
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] nvme-pci: Adjusting ctrl deref count
  2023-04-25 21:18 [PATCH 0/3] Adjusting deref count in nvme_init_ctrl Irvin Cote
  2023-04-25 21:18 ` [PATCH 1/3] nvme-core: nvme_init_ctrl cleanup Irvin Cote
@ 2023-04-25 21:18 ` Irvin Cote
  2023-04-25 21:18 ` [PATCH 3/3] nvme-core: preventing double freeing in ctrl release Irvin Cote
  2 siblings, 0 replies; 6+ messages in thread
From: Irvin Cote @ 2023-04-25 21:18 UTC (permalink / raw)
  To: hch; +Cc: kbusch, axboe, sagi, linux-nvme, chaitanyak, Irvin Cote

If there is a failure in nvme_init_ctrl
the dereference counting is not properly
handled and the code exits with a non-zero
ctrl ref-count. This is because nvme_init_ctrl
takes two refs but only accounts for one deref
in its teardown path. Instead of adding another deref
there in the core layer, we take care of it here and
we make sure that it is the last thing we do so that
all teardown logic executes before the release of
the controller as the two have some identical instructions.

Signed-off-by: Irvin Cote <irvincoteg@gmail.com>
---
 drivers/nvme/host/pci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index cd7873de3121..65e4b9f1b632 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2975,6 +2975,7 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,
 	kfree(dev->queues);
 out_free_dev:
 	kfree(dev);
+	nvme_put_ctrl(&dev->ctrl);
 	return ERR_PTR(ret);
 }
 
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] nvme-core: preventing double freeing in ctrl release
  2023-04-25 21:18 [PATCH 0/3] Adjusting deref count in nvme_init_ctrl Irvin Cote
  2023-04-25 21:18 ` [PATCH 1/3] nvme-core: nvme_init_ctrl cleanup Irvin Cote
  2023-04-25 21:18 ` [PATCH 2/3] nvme-pci: Adjusting ctrl deref count Irvin Cote
@ 2023-04-25 21:18 ` Irvin Cote
  2023-04-25 21:45   ` Keith Busch
  2 siblings, 1 reply; 6+ messages in thread
From: Irvin Cote @ 2023-04-25 21:18 UTC (permalink / raw)
  To: hch; +Cc: kbusch, axboe, sagi, linux-nvme, chaitanyak, Irvin Cote

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.

Signed-off-by: Irvin Cote <irvincoteg@gmail.com>
---
 drivers/nvme/host/core.c | 7 +++++--
 drivers/nvme/host/pci.c  | 8 ++++++--
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 353443250d48..15df7846fde8 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -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;
 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);
 }
 
 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);
 }
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] nvme-core: preventing double freeing in ctrl release
  2023-04-25 21:18 ` [PATCH 3/3] nvme-core: preventing double freeing in ctrl release Irvin Cote
@ 2023-04-25 21:45   ` Keith Busch
  2023-04-25 23:19     ` irvin cote
  0 siblings, 1 reply; 6+ messages in thread
From: Keith Busch @ 2023-04-25 21:45 UTC (permalink / raw)
  To: Irvin Cote; +Cc: hch, axboe, sagi, linux-nvme, chaitanyak

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);
>  }
> -- 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] nvme-core: preventing double freeing in ctrl release
  2023-04-25 21:45   ` Keith Busch
@ 2023-04-25 23:19     ` irvin cote
  0 siblings, 0 replies; 6+ messages in thread
From: irvin cote @ 2023-04-25 23:19 UTC (permalink / raw)
  To: Keith Busch; +Cc: hch, axboe, sagi, linux-nvme, chaitanyak

On Tue, 25 Apr 2023 at 18:45, Keith Busch <kbusch@kernel.org> wrote:

> 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?

The nvme_put_ctrl at the label "out_free_name" in nvme_init_ctrl,
doesn't decrement the ref-count to 0 (thus doesn't trigger anything).
This is because by the time we get to it device_initialize and
nvme_get_ctrl have each taken 1 reference.
However the problem indeed arises from the fact that both the teardown
path and the release method (nvme_free_ctrl, triggered by the last
nvme_put_ctrl)
call ida_free.

> 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.
If we were to put nvme_get_ctrl at the end of nvme_init_ctrl, we would
still be facing the problem.
This is because, ultimately the teardown path must clear the ref-count
of the controller before exiting the driver
and this will lead to the release method being called and doubling
some of the unwinding done by the teardown path,
among which ida_free.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-04-25 23:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-25 21:18 [PATCH 0/3] Adjusting deref count in nvme_init_ctrl Irvin Cote
2023-04-25 21:18 ` [PATCH 1/3] nvme-core: nvme_init_ctrl cleanup Irvin Cote
2023-04-25 21:18 ` [PATCH 2/3] nvme-pci: Adjusting ctrl deref count Irvin Cote
2023-04-25 21:18 ` [PATCH 3/3] nvme-core: preventing double freeing in ctrl release Irvin Cote
2023-04-25 21:45   ` Keith Busch
2023-04-25 23:19     ` irvin cote

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox