* [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect
@ 2026-10-29 6:05 Christian Aurich Zanettini Martins
2026-10-29 10:41 ` Nilay Shroff
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Christian Aurich Zanettini Martins @ 2026-10-29 6:05 UTC (permalink / raw)
To: linux-nvme
Cc: kbusch, axboe, hch, sagi, sgogte, hare, nilay, wagi, linux-kernel,
Christian Aurich Zanettini Martins, stable
Since commit 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation
and startup"), a reconnect that gets more I/O queues than before grows
the tag set before the new queues are set up. queue->ctrl is only set
by nvme_rdma_setup_io_queues(), so I/O submitted in between reaches
nvme_rdma_queue_rq() on a queue that was never allocated and
dereferences a NULL queue->ctrl:
BUG: kernel NULL pointer dereference, address: 000000000000034c
RIP: 0010:nvme_rdma_queue_rq+0x52/0xb90
Add nvme_rdma_init_queue() for the queue fields that do not depend on
the connection and call it for every queue when allocating the
controller. Such requests are then requeued like on any other queue
that is not live. queue_lock moves there as well, and
nvme_rdma_exit_queue() destroys it when the controller is freed.
Fixes: 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation and startup")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
---
Based on nvme-7.4 (89480d137bf0), also applies to mainline and v7.3.
Same approach as "nvme-tcp: fix NULL queue->ctrl dereference on
reconnect" in nvme-7.4.
pi_support, cmnd_capsule_len, queue_size and cm_done are still set on
each connect, as they depend on the RDMA device or on the connect.
Reproduced in QEMU with nvmet-rdma over rxe and
CONFIG_NVME_MULTIPATH=n: connect with attr_qid_max=2, keep direct reads
running on the CPUs that map to the new hw queues, then raise
attr_qid_max to 4. The window is short, so the readers run at
SCHED_FIFO with nvme-wq pinned to their CPUs. Without the patch the
host oopses within two tries in all six boots. With it, 64 of 64 tries
go live and 30 resets under I/O pass, with and without lockdep.
drivers/nvme/host/rdma.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 07216890f75c..79ef8927d3f0 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -90,6 +90,9 @@ enum nvme_rdma_queue_flags {
NVME_RDMA_Q_TR_READY = 2,
};
+#define NVME_RDMA_OPT_NR_QUEUES(opts) ((opts)->nr_io_queues + \
+ (opts)->nr_write_queues + (opts)->nr_poll_queues + 1)
+
struct nvme_rdma_queue {
struct nvme_rdma_qe *rsp_ring;
int queue_size;
@@ -576,6 +579,19 @@ static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue)
return ret;
}
+static void nvme_rdma_init_queue(struct nvme_rdma_ctrl *ctrl, int idx)
+{
+ struct nvme_rdma_queue *queue = &ctrl->queues[idx];
+
+ queue->ctrl = ctrl;
+ mutex_init(&queue->queue_lock);
+}
+
+static void nvme_rdma_exit_queue(struct nvme_rdma_ctrl *ctrl, int idx)
+{
+ mutex_destroy(&ctrl->queues[idx].queue_lock);
+}
+
static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
{
struct nvme_rdma_ctrl *ctrl = queue->ctrl;
@@ -583,7 +599,6 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
struct sockaddr *src_addr = NULL;
int ret;
- mutex_init(&queue->queue_lock);
if (idx && ctrl->ctrl.max_integrity_segments)
queue->pi_support = true;
else
@@ -600,8 +615,7 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
if (IS_ERR(queue->cm_id)) {
dev_info(ctrl->ctrl.device,
"failed to create CM ID: %ld\n", PTR_ERR(queue->cm_id));
- ret = PTR_ERR(queue->cm_id);
- goto out_destroy_mutex;
+ return PTR_ERR(queue->cm_id);
}
if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR)
@@ -631,8 +645,6 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
out_destroy_cm_id:
rdma_destroy_id(queue->cm_id);
nvme_rdma_destroy_queue_ib(queue);
-out_destroy_mutex:
- mutex_destroy(&queue->queue_lock);
return ret;
}
@@ -660,7 +672,6 @@ static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue)
rdma_destroy_id(queue->cm_id);
nvme_rdma_destroy_queue_ib(queue);
- mutex_destroy(&queue->queue_lock);
}
static void nvme_rdma_free_io_queues(struct nvme_rdma_ctrl *ctrl)
@@ -735,7 +746,6 @@ static int nvme_rdma_setup_io_queues(struct nvme_rdma_ctrl *ctrl,
for (i = 0; i < nr_queues; i++) {
struct nvme_rdma_queue *queue = &ctrl->queues[first + i];
- queue->ctrl = ctrl;
queue->queue_size = queue_size;
ctxs[i].queue = queue;
@@ -798,7 +808,6 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl,
bool pi_capable = false;
int error;
- ctrl->queues[0].ctrl = ctrl;
ctrl->queues[0].queue_size = NVME_AQ_DEPTH;
error = nvme_rdma_alloc_queue(&ctrl->queues[0]);
if (error)
@@ -1001,6 +1010,7 @@ static void nvme_rdma_stop_ctrl(struct nvme_ctrl *nctrl)
static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
+ int i, nr_queues = NVME_RDMA_OPT_NR_QUEUES(nctrl->opts);
if (list_empty_careful(&ctrl->list))
goto free_ctrl;
@@ -1011,6 +1021,8 @@ static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
nvmf_free_options(nctrl->opts);
free_ctrl:
+ for (i = 0; i < nr_queues; i++)
+ nvme_rdma_exit_queue(ctrl, i);
kfree(ctrl->queues);
kfree(ctrl);
}
@@ -2284,7 +2296,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
struct nvmf_ctrl_options *opts)
{
struct nvme_rdma_ctrl *ctrl;
- int ret;
+ int ret, i;
ctrl = kzalloc_obj(*ctrl);
if (!ctrl)
@@ -2333,8 +2345,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
INIT_WORK(&ctrl->err_work, nvme_rdma_error_recovery_work);
INIT_WORK(&ctrl->ctrl.reset_work, nvme_rdma_reset_ctrl_work);
- ctrl->ctrl.queue_count = opts->nr_io_queues + opts->nr_write_queues +
- opts->nr_poll_queues + 1;
+ ctrl->ctrl.queue_count = NVME_RDMA_OPT_NR_QUEUES(opts);
ctrl->ctrl.sqsize = opts->queue_size - 1;
ctrl->ctrl.kato = opts->kato;
@@ -2343,6 +2354,9 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
if (!ctrl->queues)
goto out_free_ctrl;
+ for (i = 0; i < ctrl->ctrl.queue_count; i++)
+ nvme_rdma_init_queue(ctrl, i);
+
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_rdma_ctrl_ops,
0 /* no quirks, we're perfect! */);
if (ret)
base-commit: 89480d137bf0d943a82c64f86ae2407a15276b5e
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect
2026-10-29 6:05 [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect Christian Aurich Zanettini Martins
@ 2026-10-29 10:41 ` Nilay Shroff
2026-10-29 12:16 ` Christoph Hellwig
2026-10-29 14:18 ` Christian Aurich Zanettini Martins
2 siblings, 0 replies; 4+ messages in thread
From: Nilay Shroff @ 2026-10-29 10:41 UTC (permalink / raw)
To: Christian Aurich Zanettini Martins, linux-nvme
Cc: kbusch, axboe, hch, sagi, sgogte, hare, wagi, linux-kernel,
stable
On 10/29/26 11:35 AM, Christian Aurich Zanettini Martins wrote:
> Since commit 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation
> and startup"), a reconnect that gets more I/O queues than before grows
> the tag set before the new queues are set up. queue->ctrl is only set
> by nvme_rdma_setup_io_queues(), so I/O submitted in between reaches
> nvme_rdma_queue_rq() on a queue that was never allocated and
> dereferences a NULL queue->ctrl:
>
> BUG: kernel NULL pointer dereference, address: 000000000000034c
> RIP: 0010:nvme_rdma_queue_rq+0x52/0xb90
>
> Add nvme_rdma_init_queue() for the queue fields that do not depend on
> the connection and call it for every queue when allocating the
> controller. Such requests are then requeued like on any other queue
> that is not live. queue_lock moves there as well, and
> nvme_rdma_exit_queue() destroys it when the controller is freed.
>
> Fixes: 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation and startup")
> 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] 4+ messages in thread
* Re: [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect
2026-10-29 6:05 [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect Christian Aurich Zanettini Martins
2026-10-29 10:41 ` Nilay Shroff
@ 2026-10-29 12:16 ` Christoph Hellwig
2026-10-29 14:18 ` Christian Aurich Zanettini Martins
2 siblings, 0 replies; 4+ messages in thread
From: Christoph Hellwig @ 2026-10-29 12:16 UTC (permalink / raw)
To: Christian Aurich Zanettini Martins
Cc: linux-nvme, kbusch, axboe, hch, sagi, sgogte, hare, nilay, wagi,
linux-kernel, stable
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect
2026-10-29 6:05 [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect Christian Aurich Zanettini Martins
2026-10-29 10:41 ` Nilay Shroff
2026-10-29 12:16 ` Christoph Hellwig
@ 2026-10-29 14:18 ` Christian Aurich Zanettini Martins
2 siblings, 0 replies; 4+ messages in thread
From: Christian Aurich Zanettini Martins @ 2026-10-29 14:18 UTC (permalink / raw)
To: linux-nvme
Cc: kbusch, axboe, hch, sagi, sgogte, hare, nilay, wagi, linux-kernel
On Thu, Oct 29, 2026 at 03:05:21AM -0300, Christian Aurich Zanettini Martins wrote:
> static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
> {
> struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
> + int i, nr_queues = NVME_RDMA_OPT_NR_QUEUES(nctrl->opts);
Please drop this one, I'll send a v2. Thanks for the reviews, Nilay
and Christoph.
When creating the controller fails, nvmf_create_ctrl() frees opts right
after nvme_put_ctrl(), but that put is not always the last one. With
CONFIG_DEBUG_KOBJECT_RELEASE, KASAN reports a use-after-free here on
every failed connect.
v2 only moves queue->ctrl and leaves queue_lock where it is.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-29 14:19 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 6:05 [PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect Christian Aurich Zanettini Martins
2026-10-29 10:41 ` Nilay Shroff
2026-10-29 12:16 ` Christoph Hellwig
2026-10-29 14:18 ` Christian Aurich Zanettini Martins
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.