Skip to contentSkip to conversation

nvme: Don't use a stack buffer for keep-alive command

PATCH New Roland Dreier 4 participants 10 messages Jan 11, 2018 – Feb 12, 2018 1 message not in the archive NVMe

lore
Share & tools
lore
#0 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

From: Roland Dreier <[email protected]>

In nvme_keep_alive() we pass a request with a pointer to an NVMe command on the stack into blk_execute_rq_nowait(). However, the block layer doesn't guarantee that the request is fully queued before blk_execute_rq_nowait() returns. If not, and the request is queued after nvme_keep_alive() returns, then we'll end up using stack memory that might have been overwritten to form the NVMe command we pass to hardware.

Fix this by keeping a special command struct in the nvme_ctrl struct right next to the delayed work struct used for keep-alives.

Signed-off-by Roland Dreier <roland at purestorage.com>
---
 drivers/nvme/host/core.c | 8 +++-----
 drivers/nvme/host/nvme.h | 1 +
 2 files changed, 4 insertions(+), 5 deletions(-)
drivers/nvme/host/core.c +3 -5
index 839650e0926a..329f1dd5c421 100644
@@ -748,13 +748,9 @@ static void nvme_keep_alive_end_io(struct request *rq, blk_status_t status)
 
 static int nvme_keep_alive(struct nvme_ctrl *ctrl)
 {
-	struct nvme_command c;
 	struct request *rq;
 
-	memset(&c, 0, sizeof(c));
-	c.common.opcode = nvme_admin_keep_alive;
-
-	rq = nvme_alloc_request(ctrl->admin_q, &c, BLK_MQ_REQ_RESERVED,
+	rq = nvme_alloc_request(ctrl->admin_q, &ctrl->ka_cmd, BLK_MQ_REQ_RESERVED,
 			NVME_QID_ANY);
 	if (IS_ERR(rq))
 		return PTR_ERR(rq);
@@ -786,6 +782,8 @@ void nvme_start_keep_alive(struct nvme_ctrl *ctrl)
 		return;
 
 	INIT_DELAYED_WORK(&ctrl->ka_work, nvme_keep_alive_work);
+	memset(&ctrl->ka_cmd, 0, sizeof(ctrl->ka_cmd));
+	ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive;
 	schedule_delayed_work(&ctrl->ka_work, ctrl->kato * HZ);
 }
 EXPORT_SYMBOL_GPL(nvme_start_keep_alive);
drivers/nvme/host/nvme.h +1 -0
index a00eabd06427..f524c69339b5 100644
@@ -180,6 +180,7 @@ struct nvme_ctrl {
 	struct work_struct scan_work;
 	struct work_struct async_event_work;
 	struct delayed_work ka_work;
+	struct nvme_command ka_cmd;
 	struct work_struct fw_act_work;
 
 	/* Power saving configuration */
Signature · 1 line
2.14.1
maintainer
#1 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 10 lines

From: Roland Dreier <roland at purestorage.com>

In nvme_keep_alive() we pass a request with a pointer to an NVMe command on the stack into blk_execute_rq_nowait(). However, the block layer doesn't guarantee that the request is fully queued before blk_execute_rq_nowait() returns. If not, and the request is queued after nvme_keep_alive() returns, then we'll end up using stack memory that might have been overwritten to form the NVMe command we pass to hardware.

Quoted 2 lines

Fix this by keeping a special command struct in the nvme_ctrl struct right next to the delayed work struct used for keep-alives.

Thanks Roland,

Reviewed-by Sagi Grimberg <sagi at grimberg.me>
#2 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 3 lines · Sagi Grimberg

Thanks Roland,

Reviewed-by Sagi Grimberg <sagi at grimberg.me>

I think we'll need to fix this properly and embedd the struct nvme_command into struct nvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.

A message that is not in the archive (sent off-list or not yet received): CAG4TOxOtFqM-RAdS_r1hsPVuru_=abDtTUmB=XqGFaanBrqbEQ@mail.gmail.com

#4 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 8 lines · Roland Dreier
Quoted 3 lines

I think we'll need to fix this properly and embedd the struct nvme_command into struct nvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.

I don't understand. Are there other places that submit requests with a pointer to stack memory? I haven't audited everything but I don't know of any places that submit a command and then free it before getting status back.

Every caller of nvme_alloc_request (except for lightnvm) uses stack memory, but at least the __nvme_submit_sync_cmd and nvme_submit_user_cmd synchronously wait for the completion, so it doesn't matter. That leaves nvme_keep_alive, nvme_timeout and nvme_delete_queue as problematic.

I suspect the right answer is to embedd a struct nvme_command into struct nvme_request instead of just pointing to it.

#5 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

In reply to Christoph Hellwig
Quoted 8 lines · Christoph Hellwig
Quoted 3 lines · Sagi Grimberg

Thanks Roland,

Reviewed-by Sagi Grimberg <sagi at grimberg.me>

I think we'll need to fix this properly and embedd the struct nvme_command into struct nvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.

We needn't worry about the DNR case since driver allocated commands are flagged "REQ_FAILFAST_DRIVER" and are never retried.

Allocating the full 64-byte NVMe command for each request seems a bit excessive. Since it only really applies to async driver commands, and there are so few of those as it is, I'm okay with having special cases for these as Roland suggests. Sound okay?

maintainer
#6 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 3 lines

We needn't worry about the DNR case since driver allocated commands are flagged "REQ_FAILFAST_DRIVER" and are never retried.

Yea.

Quoted 2 lines

Allocating the full 64-byte NVMe command for each request seems a bit excessive.

Why?

Quoted 3 lines

Since it only really applies to async driver commands, and there are so few of those as it is, I'm okay with having special cases for these as Roland suggests. Sound okay?

I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's nvme_delete_queue?

#7 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 4 lines · Sagi Grimberg
Quoted 2 lines

Allocating the full 64-byte NVMe command for each request seems a bit excessive.

Why?

That's 64B per request that would rarely ever get used. If you've enough drives with high queue counts, this can add up to quite a lot of memory.

#8 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

In reply to Sagi Grimberg
Quoted 2 lines · Sagi Grimberg

I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's nvme_delete_queue?

Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct nvme_queue would be the right thing to do.

maintainer
#9 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 7 lines
Quoted 3 lines

I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's nvme_delete_queue?

Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct nvme_queue would be the right thing to do.

So are you fine with picking Roland's patch to 4.16-rc?

Christoph?

#10 lore raw

PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command

Quoted 8 lines · Sagi Grimberg
Quoted 6 lines
Quoted 2 lines

I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's nvme_delete_queue?

Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct nvme_queue would be the right thing to do.

So are you fine with picking Roland's patch to 4.16-rc?

I am okay with it. If we need to change this interface in the long term, this is looks like an okay stop-gap until then.

Quoted 1 line

Christoph?