From: Keith Busch <[email protected]> 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: [email protected] Reported-by: Feifan Qian <[email protected]> Reported-by: boy juju <[email protected]> Signed-off-by: Keith Busch <[email protected]> Reviewed-by: Klaus Jensen <[email protected]> Signed-off-by: Klaus Jensen <[email protected]> (cherry picked from commit 034baf047fe143c2713f35fa640407d9da2f6fc3) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 28cc8d0e7dc..76d024c81e5 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1088,6 +1088,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: @@ -1168,6 +1170,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: -- 2.47.3
