@samitouri / QOSamiQemu / commits / f404bf0e65

virtio-scsi: fix SCSIRequest leak on a bad request

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 <pbonzini@redhat.com> Cc: Fam Zheng <fam@euphon.net> Cc: Greg Kurz <groug@kaod.org> Reported-by: Feifan Qian <bea1e@proton.me> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <5092cd4716e08d29731bfe85eea82a732b837ff4.1784895264.git.mst@redhat.com>

Michael S. Tsirkin committed Jul 8, 2026 at 11:35 UTC f404bf0e6504e0412a00ea64708f17d2a5e3f869
3 files changed +10
hw/scsi/scsi-bus.c
+7
@@ -1513,6 +1513,13 @@ void scsi_req_unref(SCSIRequest *req)
1513 }
1514 }
1515
1516 +void scsi_req_unref_detach_hba(SCSIRequest *req)
1517 +{
1518 + /* Unref when the HBA frees hba_private separately (e.g. virtio_scsi_free_req) */
1519 + req->hba_private = NULL;
1520 + scsi_req_unref(req);
1521 +}
1522 +
1523 /* Tell the device that we finished processing this chunk of I/O. It
1524 will start the next chunk or complete the command. */
1525 void scsi_req_continue(SCSIRequest *req)
hw/scsi/virtio-scsi.c
+2
@@ -931,7 +931,9 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
931 req = QTAILQ_FIRST(&reqs);
932 QTAILQ_REMOVE(&reqs, req, next);
933 defer_call_end();
934 + /* Drop both the ref from _prepare and the initial ref */
935 scsi_req_unref(req->sreq);
936 + scsi_req_unref_detach_hba(req->sreq);
937 virtqueue_detach_element(req->vq, &req->elem, 0);
938 virtio_scsi_free_req(req);
939 }
include/hw/scsi/scsi.h
+1
@@ -221,6 +221,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
221 int32_t scsi_req_enqueue(SCSIRequest *req);
222 SCSIRequest *scsi_req_ref(SCSIRequest *req);
223 void scsi_req_unref(SCSIRequest *req);
224 +void scsi_req_unref_detach_hba(SCSIRequest *req);
225
226 int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
227 size_t buf_len, void *hba_private);