Commit f404bf0e65 for qemu.org

commit f404bf0e6504e0412a00ea64708f17d2a5e3f869
Author: Michael S. Tsirkin <mst@redhat.com>
Date:   Wed Jul 8 11:35:57 2026 -0400

    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>

diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index dccb2f25b2..deb43d5560 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1513,6 +1513,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 6c73768011..bf64d1231a 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -931,7 +931,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 5f83e58d1d..c60c6e8810 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -221,6 +221,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);