From: "Michael S. Tsirkin" <[email protected]>
When virtio_scsi_handle_cmd_vq() cleans up prepared requests after a
malformed element in the same batch, it drops only one reference even
though virtio_scsi_handle_cmd_req_prepare() leaves each unsubmitted
SCSIRequest with two references. This leaks the request and allows
repeated bad batches to cause unbounded host memory growth.
Add a second scsi_req_unref() and clear hba_private first.
Fixes: CVE-2026-61476
Fixes: 661e32fb3c ("virtio-scsi: convert virtio_scsi_bad_req() to use
virtio_error()")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3875
Cc: Paolo Bonzini <[email protected]>
Cc: Fam Zheng <[email protected]>
Cc: Greg Kurz <[email protected]>
Reported-by: Feifan Qian <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
Message-ID:
<5092cd4716e08d29731bfe85eea82a732b837ff4.1784895264.git....@redhat.com>
(cherry picked from commit f404bf0e6504e0412a00ea64708f17d2a5e3f869)
Signed-off-by: Michael Tokarev <[email protected]>
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 35ff216510e..259fab3c7bd 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1515,6 +1515,13 @@ void scsi_req_unref(SCSIRequest *req)
}
}
+void scsi_req_unref_detach_hba(SCSIRequest *req)
+{
+ /* Unref when the HBA frees hba_private separately (e.g.
virtio_scsi_free_req) */
+ req->hba_private = NULL;
+ scsi_req_unref(req);
+}
+
/* Tell the device that we finished processing this chunk of I/O. It
will start the next chunk or complete the command. */
void scsi_req_continue(SCSIRequest *req)
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index ffa85fa25a5..454e7c9c869 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -935,7 +935,9 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s,
VirtQueue *vq)
req = QTAILQ_FIRST(&reqs);
QTAILQ_REMOVE(&reqs, req, next);
defer_call_end();
+ /* Drop both the ref from _prepare and the initial ref */
scsi_req_unref(req->sreq);
+ scsi_req_unref_detach_hba(req->sreq);
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_scsi_free_req(req);
}
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 90ee192b4d4..3c7dc845783 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -211,6 +211,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag,
uint32_t lun,
int32_t scsi_req_enqueue(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
void scsi_req_unref(SCSIRequest *req);
+void scsi_req_unref_detach_hba(SCSIRequest *req);
int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
size_t buf_len, void *hba_private);
--
2.47.3