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;
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);