lib/nvmf: fix identify command corruption

In the previous fix:

adc2942ad nvmf: nvmf_ctrlr_get_log_page use iovs to store the log page

a data corruption bug in the log page code was fixed. Previously, it
used req->data, which may be too short a buffer in the case that the
buffer is split across more than one IOV. req->data is never safe to use
in this situation. The code was changed to use the provided iovs instead
of req->data.

However, the identify command handling was still vulnerable to this
problem, and has been seen in real life at least with a CentOS guest VM.

The fix is basically the same: use the IOV utility functions to write
out the response instead of directly trying to use req->data.

Signed-off-by: John Levon <john.levon@nutanix.com>
Change-Id: I00445895af20e43be73189629576eee0667f86dd
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16121
Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
John Levon 2023-01-04 10:12:32 +00:00 committed by Jim Harris
parent 56fe6fdf85
commit acc4d1766c

View File

@ -2877,8 +2877,10 @@ nvmf_ctrlr_identify_ns_id_descriptor_list(
ADD_ID_DESC(SPDK_NVME_NIDT_UUID, &ns->opts.uuid, sizeof(ns->opts.uuid));
/*
* The list is automatically 0-terminated because controller to host buffers in
* admin commands always get zeroed in nvmf_ctrlr_process_admin_cmd().
* The list is automatically 0-terminated, both in the temporary buffer
* used by nvmf_ctrlr_identify(), and the eventual iov destination -
* controller to host buffers in admin commands always get zeroed in
* nvmf_ctrlr_process_admin_cmd().
*/
#undef ADD_ID_DESC
@ -2894,12 +2896,15 @@ nvmf_ctrlr_identify(struct spdk_nvmf_request *req)
struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
struct spdk_nvmf_subsystem *subsystem = ctrlr->subsys;
int ret = SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
char tmpbuf[SPDK_NVME_IDENTIFY_BUFLEN] = "";
struct copy_iovs_ctx copy_ctx;
if (req->data == NULL || req->length < SPDK_NVME_IDENTIFY_BUFLEN) {
SPDK_DEBUGLOG(nvmf, "identify command with invalid buffer\n");
rsp->status.sct = SPDK_NVME_SCT_GENERIC;
rsp->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
return ret;
}
cns = cmd->cdw10_bits.identify.cns;
@ -2910,24 +2915,41 @@ nvmf_ctrlr_identify(struct spdk_nvmf_request *req)
goto invalid_cns;
}
/*
* We must use a temporary buffer: it's entirely possible the out buffer
* is split across more than one IOV.
*/
_init_copy_iovs_ctx(&copy_ctx, req->iov, req->iovcnt);
switch (cns) {
case SPDK_NVME_IDENTIFY_NS:
return spdk_nvmf_ctrlr_identify_ns(ctrlr, cmd, rsp, req->data);
ret = spdk_nvmf_ctrlr_identify_ns(ctrlr, cmd, rsp, (void *)&tmpbuf);
break;
case SPDK_NVME_IDENTIFY_CTRLR:
return spdk_nvmf_ctrlr_identify_ctrlr(ctrlr, req->data);
ret = spdk_nvmf_ctrlr_identify_ctrlr(ctrlr, (void *)&tmpbuf);
break;
case SPDK_NVME_IDENTIFY_ACTIVE_NS_LIST:
return nvmf_ctrlr_identify_active_ns_list(subsystem, cmd, rsp, req->data);
ret = nvmf_ctrlr_identify_active_ns_list(subsystem, cmd, rsp, (void *)&tmpbuf);
break;
case SPDK_NVME_IDENTIFY_NS_ID_DESCRIPTOR_LIST:
return nvmf_ctrlr_identify_ns_id_descriptor_list(subsystem, cmd, rsp, req->data, req->length);
ret = nvmf_ctrlr_identify_ns_id_descriptor_list(subsystem, cmd, rsp,
tmpbuf, req->length);
break;
default:
goto invalid_cns;
}
if (ret == SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE) {
_copy_buf_to_iovs(&copy_ctx, tmpbuf, sizeof(tmpbuf));
}
return ret;
invalid_cns:
SPDK_DEBUGLOG(nvmf, "Identify command with unsupported CNS 0x%02x\n", cns);
rsp->status.sct = SPDK_NVME_SCT_GENERIC;
rsp->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
return ret;
}
static bool