diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index d60a680dbc..7845e59e4b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -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: