mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] nvme-tcp: fix use-after-free in nvme_tcp_free_ctrl()
@ 2026-10-30 17:01 Christian Aurich Zanettini Martins
  2026-10-30 17:43 ` Nilay Shroff
  0 siblings, 1 reply; 2+ messages in thread
From: Christian Aurich Zanettini Martins @ 2026-10-30 17:01 UTC (permalink / raw)
  To: linux-nvme
  Cc: kbusch, axboe, hch, sagi, hare, nilay, wagi, kanie, linux-kernel,
	Christian Aurich Zanettini Martins, stable

nvme_tcp_free_ctrl() reads ctrl->ctrl.opts to know how many queue
mutexes to destroy. When creating the controller fails,
nvmf_create_ctrl() frees opts as soon as nvme_tcp_create_ctrl()
returns, but a concurrent open of the character device can hold the
last reference to the controller. With CONFIG_DEBUG_KOBJECT_RELEASE,
which delays the release, every failed connect reads freed memory:

  BUG: KASAN: slab-use-after-free in nvme_tcp_free_ctrl+0x38b/0x430
  Read of size 4 at addr ffff888005b93740 by task kworker/2:1/51

Keep the size of the queue array in the controller and use it instead.

Fixes: 2eeeaa9c1ff0 ("nvme-tcp: Validate the queue state under the queue lock in the address attribute")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
---
Based on nvme-7.4. Nilay pointed this out on the nvme-rdma thread and
suggested the same fix:
https://lore.kernel.org/linux-nvme/1408da28-a55e-448b-b493-d60fd5d20524@linux.ibm.com/

Reproduced in QEMU with KASAN and CONFIG_DEBUG_KOBJECT_RELEASE by
connecting to a port with no listener. Each failed connect reports
three use-after-free reads without the patch and none with it. Growing
attr_qid_max from 2 to 4 and 30 resets, both under I/O, still pass with
and without lockdep.

 drivers/nvme/host/tcp.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index a8e94e4866db..d11fb52ce1a2 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -204,6 +204,7 @@ struct nvme_tcp_ctrl {
 	struct delayed_work	connect_work;
 	struct nvme_tcp_request async_req;
 	u32			io_queues[HCTX_MAX_TYPES];
+	unsigned int		nr_allocated_queues;
 	struct net		*net;
 };
 
@@ -2861,8 +2862,6 @@ static void nvme_tcp_stop_ctrl(struct nvme_ctrl *ctrl)
 static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
 {
 	struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
-	/* the same count used to size and initialize the queue array */
-	unsigned int nr_queues = NVME_TCP_OPT_NR_QUEUES(nctrl->opts);
 	unsigned int i;
 
 	if (list_empty_careful(&ctrl->list))
@@ -2875,7 +2874,7 @@ static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
 	nvmf_free_options(nctrl->opts);
 free_ctrl:
 	put_net(ctrl->net);
-	for (i = 0; i < nr_queues; i++)
+	for (i = 0; i < ctrl->nr_allocated_queues; i++)
 		nvme_tcp_exit_queue(ctrl, i);
 	kfree(ctrl->queues);
 	kfree(ctrl);
@@ -3269,13 +3268,14 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
 		goto out_free_ctrl;
 	}
 
-	ctrl->queues = kzalloc_objs(*ctrl->queues, ctrl->ctrl.queue_count);
+	ctrl->nr_allocated_queues = ctrl->ctrl.queue_count;
+	ctrl->queues = kzalloc_objs(*ctrl->queues, ctrl->nr_allocated_queues);
 	if (!ctrl->queues) {
 		ret = -ENOMEM;
 		goto out_free_ctrl;
 	}
 
-	for (i = 0; i < ctrl->ctrl.queue_count; i++)
+	for (i = 0; i < ctrl->nr_allocated_queues; i++)
 		nvme_tcp_init_queue(ctrl, i);
 
 	ctrl->net = get_net(current->nsproxy->net_ns);

base-commit: 89480d137bf0d943a82c64f86ae2407a15276b5e
-- 
2.43.0


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

* Re: [PATCH] nvme-tcp: fix use-after-free in nvme_tcp_free_ctrl()
  2026-10-30 17:01 [PATCH] nvme-tcp: fix use-after-free in nvme_tcp_free_ctrl() Christian Aurich Zanettini Martins
@ 2026-10-30 17:43 ` Nilay Shroff
  0 siblings, 0 replies; 2+ messages in thread
From: Nilay Shroff @ 2026-10-30 17:43 UTC (permalink / raw)
  To: Christian Aurich Zanettini Martins, linux-nvme
  Cc: kbusch, axboe, hch, sagi, hare, wagi, kanie, linux-kernel, stable

On 10/30/26 10:31 PM, Christian Aurich Zanettini Martins wrote:
> nvme_tcp_free_ctrl() reads ctrl->ctrl.opts to know how many queue
> mutexes to destroy. When creating the controller fails,
> nvmf_create_ctrl() frees opts as soon as nvme_tcp_create_ctrl()
> returns, but a concurrent open of the character device can hold the
> last reference to the controller. With CONFIG_DEBUG_KOBJECT_RELEASE,
> which delays the release, every failed connect reads freed memory:
> 
>    BUG: KASAN: slab-use-after-free in nvme_tcp_free_ctrl+0x38b/0x430
>    Read of size 4 at addr ffff888005b93740 by task kworker/2:1/51
> 
> Keep the size of the queue array in the controller and use it instead.
> 
> Fixes: 2eeeaa9c1ff0 ("nvme-tcp: Validate the queue state under the queue lock in the address attribute")
> Cc:stable@vger.kernel.org
> Signed-off-by: Christian Aurich Zanettini Martins<christian.aurichzm@gmail.com>

Looks good to me:
Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>

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

end of thread, other threads:[~2026-10-30 17:43 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 17:01 [PATCH] nvme-tcp: fix use-after-free in nvme_tcp_free_ctrl() Christian Aurich Zanettini Martins
2026-10-30 17:43 ` Nilay Shroff

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

all inboxes | Powered by JetHome®