]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
hw/nvme: ensure sgl forward progress
authorKeith Busch <kbusch@kernel.org>
Fri, 29 May 2026 13:59:47 +0000 (06:59 -0700)
committerKlaus Jensen <k.jensen@samsung.com>
Mon, 6 Jul 2026 22:39:42 +0000 (00:39 +0200)
A degenerate host can create segment loops of zero-byte data descriptors
that the controller never breaks out of. While the spec allows zero
length segments, it provides no guidance on handling loops. It
makes no sense for a host to submit such a descriptor anyway since it
can and trivially should point to the next transfer segment, so don't
even try to work with such behavior. Just reject the command,
terminating the loop.

Cc: qemu-stable@nongnu.org
Reported-by: Feifan Qian <bea1e@proton.me>
Reported-by: boy juju <agx1657748706@gmail.com>
Signed-off-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
hw/nvme/ctrl.c

index d60a680dbc15a0d0c0faf4e86fb5919e83dff857..7845e59e4b08ef8fce3b15ca8b842d07e6863fe8 100644 (file)
@@ -1090,6 +1090,8 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl,
     }
 
     for (;;) {
+        size_t prev_len = len;
+
         switch (NVME_SGL_TYPE(sgld->type)) {
         case NVME_SGL_DESCR_TYPE_SEGMENT:
         case NVME_SGL_DESCR_TYPE_LAST_SEGMENT:
@@ -1170,6 +1172,17 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl,
         if (status) {
             goto unmap;
         }
+
+        /*
+         * Reject if this segment made no forward progress. The host should
+         * have skipped linking an empty segment. While not strictly spec
+         * compliant, allowing this makes it easy for a pathological host to
+         * create an infinite loop.
+         */
+        if (len == prev_len) {
+            status = NVME_INVALID_SGL_SEG_DESCR | NVME_DNR;
+            goto unmap;
+        }
     }
 
 out: