summaryrefslogtreecommitdiff
path: root/drivers/nvme/target
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nvme/target')
-rw-r--r--drivers/nvme/target/discovery.c23
-rw-r--r--drivers/nvme/target/fabrics-cmd-auth.c15
-rw-r--r--drivers/nvme/target/rdma.c6
3 files changed, 38 insertions, 6 deletions
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index e9b35549e254..114869d16a1f 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -166,6 +166,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
u64 offset = nvmet_get_log_page_offset(req->cmd);
size_t data_len = nvmet_get_log_page_len(req->cmd);
size_t alloc_len;
+ size_t copy_len;
struct nvmet_subsys_link *p;
struct nvmet_port *r;
u32 numrec = 0;
@@ -242,7 +243,27 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
up_read(&nvmet_config_sem);
- status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len);
+ /*
+ * Validate the host-supplied log page offset before copying out.
+ * Without this check, the host controls a 64-bit byte offset into
+ * a small kzalloc'd buffer: a value past the log page lets the
+ * subsequent memcpy read adjacent kernel heap, and a value aimed
+ * at unmapped kernel memory faults the in-kernel copy and crashes
+ * the target host. The Discovery controller is unauthenticated,
+ * so the bug is reachable from any reachable fabric peer.
+ */
+ if (offset > alloc_len) {
+ req->error_loc =
+ offsetof(struct nvme_get_log_page_command, lpo);
+ status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
+ goto out_free_buffer;
+ }
+
+ copy_len = min_t(size_t, data_len, alloc_len - offset);
+ status = nvmet_copy_to_sgl(req, 0, buffer + offset, copy_len);
+ if (!status && copy_len < data_len)
+ status = nvmet_zero_sgl(req, copy_len, data_len - copy_len);
+out_free_buffer:
kfree(buffer);
out:
nvmet_req_complete(req, status);
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index f1e613e7c63e..0a85acf1e5c7 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -132,13 +132,22 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
return 0;
}
-static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
+static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_reply_data *data = d;
- u16 dhvlen = le16_to_cpu(data->dhvlen);
+ u16 dhvlen;
u8 *response;
+ if (tl < sizeof(*data))
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
+ dhvlen = le16_to_cpu(data->dhvlen);
+
+ /* Validate that hl and dhvlen fit within the transfer length */
+ if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl)
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n",
__func__, ctrl->cntlid, req->sq->qid,
data->hl, data->cvalid, dhvlen);
@@ -338,7 +347,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
switch (data->auth_id) {
case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
- dhchap_status = nvmet_auth_reply(req, d);
+ dhchap_status = nvmet_auth_reply(req, d, tl);
if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index e6e2c3f9afdf..ac26f4f774c4 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1598,8 +1598,10 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
pending++;
}
mutex_unlock(&nvmet_rdma_queue_mutex);
- if (pending > NVMET_RDMA_BACKLOG)
- return NVME_SC_CONNECT_CTRL_BUSY;
+ if (pending > NVMET_RDMA_BACKLOG) {
+ ret = NVME_SC_CONNECT_CTRL_BUSY;
+ goto put_device;
+ }
}
ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);