[PATCH v2 1/2] nvme: fix memory corruption for passthrough metadata
Kanchan Joshi
joshi.k at samsung.com
Mon Aug 14 00:02:12 PDT 2023
User can specify a smaller meta buffer than what the device is
wired to update/access.
This may lead to Device doing a larger DMA operation, overwriting
unrelated kernel memory.
Detect this situation for uring passthrough, and bail out.
Fixes: 456cba386e94 ("nvme: wire-up uring-cmd support for io-passthru on char-device")
Cc: stable at vger.kernel.org
Reported-by: Vincent Fu <vincent.fu at samsung.com>
Signed-off-by: Kanchan Joshi <joshi.k at samsung.com>
---
drivers/nvme/host/ioctl.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 19a5177bc360..717c7effaf8a 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -320,6 +320,35 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
meta_len, lower_32_bits(io.slba), NULL, 0, 0);
}
+static bool nvme_validate_passthru_meta(struct nvme_ctrl *ctrl,
+ struct nvme_ns *ns,
+ struct nvme_command *c,
+ __u64 meta, __u32 meta_len)
+{
+ /*
+ * User may specify smaller meta-buffer with a larger data-buffer.
+ * Driver allocated meta buffer will also be small.
+ * Device can do larger dma into that, overwriting unrelated kernel
+ * memory.
+ */
+ if (ns && (meta_len || meta)) {
+ u16 nlb = lower_16_bits(le32_to_cpu(c->common.cdw12));
+ u16 control = upper_16_bits(le32_to_cpu(c->common.cdw12));
+
+ /* meta transfer from/to host is not done */
+ if (control & NVME_RW_PRINFO_PRACT && ns->ms == ns->pi_size)
+ return true;
+
+ if (meta_len != (nlb + 1) * ns->ms) {
+ dev_err(ctrl->device,
+ "%s: metadata length does not match!\n", current->comm);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool nvme_validate_passthru_nsid(struct nvme_ctrl *ctrl,
struct nvme_ns *ns, __u32 nsid)
{
@@ -593,6 +622,10 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
d.metadata_len = READ_ONCE(cmd->metadata_len);
d.timeout_ms = READ_ONCE(cmd->timeout_ms);
+ if (!nvme_validate_passthru_meta(ctrl, ns, &c, d.metadata,
+ d.metadata_len))
+ return -EINVAL;
+
if (issue_flags & IO_URING_F_NONBLOCK) {
rq_flags |= REQ_NOWAIT;
blk_flags = BLK_MQ_REQ_NOWAIT;
--
2.25.1
More information about the Linux-nvme
mailing list