All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] Harden virtio migration load paths against crafted streams
@ 2026-07-29 23:18 Laurent Vivier
  2026-07-29 23:18 ` [PATCH v3 1/7] VirtioDeviceClass: Add an Error parameter to vmstate load member Laurent Vivier
                   ` (8 more replies)
  0 siblings, 9 replies; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier

A crafted migration stream can crash the destination QEMU process
through unvalidated fields in the virtio device state: an unbounded
allocation in virtio-serial, reachable assertions in the shared
virtqueue element deserializer, assert()/exit(1) calls in
virtio-scsi and spapr-vscsi request loading, and missing error
propagation through the SCSI and virtio device load paths.

These are hardening fixes: the destination QEMU is in a paused
pre-start state and the source VM is unaffected by a failed migration.

Patch 1 adds an Error** parameter to the virtio and SCSI load_request
callbacks, allowing proper error propagation instead of error_report()
or silent failures. This provides the infrastructure for the remaining
patches.

Patch 2 validates the virtio-serial nr_active_ports count against the
configured maximum before allocating the post-load array.

Patches 3 and 5 are from Michael S. Tsirkin, modified to use the new
Error** parameter:

Patch 3 makes virtqueue_map() return bool instead of calling exit(1)
on mapping failures, and propagates errors through
qemu_get_virtqueue_element() to virtio-blk, virtio-serial, and
virtio-scsi.

Patch 4 replaces the assertions in qemu_get_virtqueue_element() with
a bounds check returning NULL on invalid in_num/out_num counts.

Patch 5 replaces the assertion in mptsas_load_request() with proper
error handling.

Patch 6 replaces the remaining assert() and exit(1) calls in
virtio_scsi_load_request() with proper error returns.

Patch 7 replaces the assert() calls in vscsi_load_request() with
proper error returns and propagation.

v2:
- New patch 1 to add Error** parameter to load_request callbacks
- Patches 2, 4, 6, 7: add error_setg() calls with descriptive messages
- New patches 3 and 5 from Michael S. Tsirkin (modified to use errp)
- Patch 4: remove caller updates (now handled by patch 3)
- New patch 7: harden spapr_vscsi load_request

Tested with migration round-trips for virtio-serial (0 to 511 ports),
virtio-blk (1-2 disks), virtio-scsi (1-2 disks), mptsas1068
(with and without scsi-hd), and spapr-vscsi, plus the original PoC
reproducers for issues #3801, #3802, and #3888. ppc64 qtests pass.

Laurent Vivier (5):
  VirtioDeviceClass: Add an Error parameter to vmstate load member
  hw/char/virtio-serial-bus: validate nr_active_ports from migration stream
  hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state
  hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream
  hw/scsi/spapr_vscsi: do not crash QEMU on migration errors

Michael S. Tsirkin (2):
  virtio: do not crash QEMU on migration errors
  mptsas: do not crash QEMU on migration errors

 hw/block/virtio-blk.c       | 16 ++++++--
 hw/char/virtio-serial-bus.c | 33 ++++++++++----
 hw/scsi/esp.c               |  2 +-
 hw/scsi/mptsas.c            | 18 +++++---
 hw/scsi/scsi-bus.c          | 25 +++++++++--
 hw/scsi/scsi-disk.c         | 10 +++--
 hw/scsi/scsi-generic.c      |  3 +-
 hw/scsi/spapr_vscsi.c       | 18 +++++++--
 hw/scsi/virtio-scsi.c       | 27 ++++++++++--
 hw/usb/dev-storage.c        |  2 +-
 hw/virtio/virtio.c          | 82 ++++++++++++++++++++++++++-----------
 include/hw/scsi/scsi.h      |  4 +-
 include/hw/usb/msd.h        |  2 +-
 include/hw/virtio/virtio.h  |  4 +-
 14 files changed, 182 insertions(+), 62 deletions(-)

Laurent Vivier (5):
  VirtioDeviceClass: Add an Error parameter to vmstate load member
  hw/char/virtio-serial-bus: validate nr_active_ports from migration
    stream
  hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid
    state
  hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid
    stream
  hw/scsi/spapr_vscsi: do not crash QEMU on migration errors

Michael S. Tsirkin (2):
  virtio: do not crash QEMU on migration errors
  mptsas: do not crash QEMU on migration errors

 hw/block/virtio-blk.c       | 11 +++++--
 hw/char/virtio-serial-bus.c | 30 +++++++++++------
 hw/scsi/esp.c               |  2 +-
 hw/scsi/mptsas.c            | 16 ++++++----
 hw/scsi/scsi-bus.c          | 18 +++++++++--
 hw/scsi/scsi-disk.c         |  7 ++--
 hw/scsi/scsi-generic.c      |  3 +-
 hw/scsi/spapr_vscsi.c       | 25 +++++++++++----
 hw/scsi/virtio-scsi.c       | 28 ++++++++++++----
 hw/usb/dev-storage.c        |  2 +-
 hw/virtio/virtio.c          | 64 ++++++++++++++++++++++++-------------
 include/hw/scsi/scsi.h      |  4 +--
 include/hw/usb/msd.h        |  2 +-
 include/hw/virtio/virtio.h  |  4 +--
 14 files changed, 150 insertions(+), 66 deletions(-)

-- 
2.54.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v3 1/7] VirtioDeviceClass: Add an Error parameter to vmstate load member
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
@ 2026-07-29 23:18 ` Laurent Vivier
  2026-07-29 23:18 ` [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream Laurent Vivier
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier

Replace error_report() by error_setg() when it's possible.

In scsi-bus, check if error has been set by load_request, and propagate
it to caller.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3888
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: new patch to propagate errors properly through the load_request chain
        includes changes made by Michael S. Tsirkin in
        https://lore.kernel.org/qemu-devel/3777347521a12252e228e4a6243c75250bf76626.1784898250.git.mst@redhat.com

 hw/block/virtio-blk.c       |  7 ++++---
 hw/char/virtio-serial-bus.c | 20 +++++++++++---------
 hw/scsi/esp.c               |  2 +-
 hw/scsi/mptsas.c            |  2 +-
 hw/scsi/scsi-bus.c          | 18 ++++++++++++++++--
 hw/scsi/scsi-disk.c         |  7 ++++---
 hw/scsi/scsi-generic.c      |  3 ++-
 hw/scsi/spapr_vscsi.c       |  7 ++++---
 hw/scsi/virtio-scsi.c       |  3 ++-
 hw/usb/dev-storage.c        |  2 +-
 hw/virtio/virtio.c          |  5 +++--
 include/hw/scsi/scsi.h      |  4 ++--
 include/hw/usb/msd.h        |  2 +-
 include/hw/virtio/virtio.h  |  2 +-
 14 files changed, 53 insertions(+), 31 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 6b92066aff4c..352b897c4ae5 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1364,7 +1364,7 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f)
 }
 
 static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
-                                  int version_id)
+                                  int version_id, Error **errp)
 {
     VirtIOBlock *s = VIRTIO_BLK(vdev);
 
@@ -1377,8 +1377,9 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
             vq_idx = qemu_get_be32(f);
 
             if (vq_idx >= nvqs) {
-                error_report("Invalid virtqueue index in request list: %#x",
-                             vq_idx);
+                error_setg(errp,
+                           "Invalid virtqueue index in request list: 0x%x",
+                           vq_idx);
                 return -EINVAL;
             }
         }
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index c1973f0248fc..87bfe51b6e93 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -725,8 +725,8 @@ static void virtio_serial_post_load_timer_cb(void *opaque)
     s->post_load = NULL;
 }
 
-static int fetch_active_ports_list(QEMUFile *f,
-                                   VirtIOSerial *s, uint32_t nr_active_ports)
+static int fetch_active_ports_list(QEMUFile *f, VirtIOSerial *s,
+                                   uint32_t nr_active_ports, Error **errp)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(s);
     uint32_t i;
@@ -749,6 +749,7 @@ static int fetch_active_ports_list(QEMUFile *f,
         id = qemu_get_be32(f);
         port = find_port_by_id(s, id);
         if (!port) {
+            error_setg(errp, "Invalid port id %u", id);
             return -EINVAL;
         }
 
@@ -776,7 +777,7 @@ static int fetch_active_ports_list(QEMUFile *f,
 }
 
 static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
-                                     int version_id)
+                                     int version_id, Error **errp)
 {
     VirtIOSerial *s = VIRTIO_SERIAL(vdev);
     uint32_t max_nr_ports, nr_active_ports, ports_map;
@@ -794,10 +795,8 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
         qemu_get_be32s(f, &ports_map);
 
         if (ports_map != s->ports_map[i]) {
-            /*
-             * Ports active on source and destination don't
-             * match. Fail migration.
-             */
+            error_setg(errp, "Ports active on source (%u) and destination (%u)"
+                       " don't match", ports_map, s->ports_map[i]);
             return -EINVAL;
         }
     }
@@ -805,8 +804,11 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
     qemu_get_be32s(f, &nr_active_ports);
 
     if (nr_active_ports) {
-        ret = fetch_active_ports_list(f, s, nr_active_ports);
-        if (ret) {
+        Error *local_err = NULL;
+
+        ret = fetch_active_ports_list(f, s, nr_active_ports, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
             return ret;
         }
     }
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 933271431ba0..a99bfe8e9428 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -1539,7 +1539,7 @@ static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr,
     return val;
 }
 
-static void *esp_load_request(QEMUFile *f, SCSIRequest *req)
+static void *esp_load_request(QEMUFile *f, SCSIRequest *req, Error **errp)
 {
     ESPState *s = container_of(req->bus, ESPState, bus);
 
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 5df124c0ce50..45638af0afc3 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -1230,7 +1230,7 @@ static void mptsas_save_request(QEMUFile *f, SCSIRequest *sreq)
     }
 }
 
-static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq)
+static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
 {
     SCSIBus *bus = sreq->bus;
     MPTSASState *s = container_of(bus, MPTSASState, bus);
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index deb43d5560e3..9e9bb977bee3 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1921,10 +1921,24 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size,
         req = scsi_req_new(s, tag, lun, buf, sizeof(buf), NULL);
         req->retry = (sbyte == 1);
         if (bus->info->load_request) {
-            req->hba_private = bus->info->load_request(f, req);
+            Error *local_err = NULL;
+
+            req->hba_private = bus->info->load_request(f, req, &local_err);
+            if (local_err) {
+                error_report_err(local_err);
+                scsi_req_unref(req);
+                return -1;
+            }
         }
         if (req->ops->load_request) {
-            req->ops->load_request(f, req);
+            Error *local_err = NULL;
+
+            req->ops->load_request(f, req, &local_err);
+            if (local_err) {
+                error_report_err(local_err);
+                scsi_req_unref(req);
+                return -1;
+            }
         }
 
         /* Just restart it later.  */
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 1b0cce128c5e..00f12439d1b6 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -181,7 +181,7 @@ static void scsi_disk_emulate_save_request(QEMUFile *f, SCSIRequest *req)
     }
 }
 
-static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
+static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req, Error **errp)
 {
     SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
 
@@ -204,12 +204,13 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
     qemu_iovec_init_external(&r->qiov, &r->iov, 1);
 }
 
-static void scsi_disk_emulate_load_request(QEMUFile *f, SCSIRequest *req)
+static void scsi_disk_emulate_load_request(QEMUFile *f, SCSIRequest *req,
+                                           Error **errp)
 {
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
 
     if (s->migrate_emulated_scsi_request) {
-        scsi_disk_load_request(f, req);
+        scsi_disk_load_request(f, req, errp);
     }
 }
 
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 8999f3b72006..20deb9a21250 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -53,7 +53,8 @@ static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
     }
 }
 
-static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
+static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req,
+                                      Error **errp)
 {
     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
 
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index b4c8f94d22ad..7768ec0bdd49 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -642,7 +642,7 @@ static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq)
                                    req->cur_desc_offset);
 }
 
-static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
+static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
 {
     SCSIBus *bus = sreq->bus;
     VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(bus->qbus.parent);
@@ -657,8 +657,9 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
     memset(req, 0, sizeof(*req));
     rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1, &local_err);
     if (rc) {
-        fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
-        error_report_err(local_err);
+        error_propagate_prepend(errp, local_err,
+                                "VSCSI: failed loading request tag#%u: ",
+                                sreq->tag);
         return NULL;
     }
     assert(req->active);
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index bf64d1231a81..54667dc4f51d 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -261,7 +261,8 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
     qemu_put_virtqueue_element(vdev, f, &req->elem);
 }
 
-static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
+static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq,
+                                      Error **errp)
 {
     SCSIBus *bus = sreq->bus;
     VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 040cf1505181..d74aa087de66 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -556,7 +556,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
     }
 }
 
-void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
+void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req, Error **errp)
 {
     MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
 
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index daa5607338c9..64d780b1336e 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -3610,8 +3610,9 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
     virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
 
     if (vdc->load != NULL) {
-        ret = vdc->load(vdev, f, version_id);
-        if (ret) {
+        ret = vdc->load(vdev, f, version_id, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
             return ret;
         }
     }
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index c60c6e8810ea..51ccd02951c6 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -134,7 +134,7 @@ struct SCSIReqOps {
     uint8_t *(*get_buf)(SCSIRequest *req);
 
     void (*save_request)(QEMUFile *f, SCSIRequest *req);
-    void (*load_request)(QEMUFile *f, SCSIRequest *req);
+    void (*load_request)(QEMUFile *f, SCSIRequest *req, Error **errp);
 };
 
 struct SCSIBusInfo {
@@ -150,7 +150,7 @@ struct SCSIBusInfo {
     QEMUSGList *(*get_sg_list)(SCSIRequest *req);
 
     void (*save_request)(QEMUFile *f, SCSIRequest *req);
-    void *(*load_request)(QEMUFile *f, SCSIRequest *req);
+    void *(*load_request)(QEMUFile *f, SCSIRequest *req, Error **errp);
     void (*free_request)(SCSIBus *bus, void *priv);
 
     /*
diff --git a/include/hw/usb/msd.h b/include/hw/usb/msd.h
index 125d2c218f6d..167e7d3f2fe0 100644
--- a/include/hw/usb/msd.h
+++ b/include/hw/usb/msd.h
@@ -51,5 +51,5 @@ DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV,
 void usb_msd_transfer_data(SCSIRequest *req, uint32_t len);
 void usb_msd_command_complete(SCSIRequest *req, size_t resid);
 void usb_msd_request_cancelled(SCSIRequest *req);
-void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req);
+void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req, Error **errp);
 void usb_msd_handle_reset(USBDevice *dev);
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index c99cb19d8865..bcb03154e243 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -268,7 +268,7 @@ struct VirtioDeviceClass {
      * use vmsd for new devices.
      */
     void (*save)(VirtIODevice *vdev, QEMUFile *f);
-    int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id);
+    int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id, Error **errp);
     /* Post load hook in vmsd is called early while device is processed, and
      * when VirtIODevice isn't fully initialized.  Devices should use this instead,
      * unless they specifically want to verify the migration stream as it's
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
  2026-07-29 23:18 ` [PATCH v3 1/7] VirtioDeviceClass: Add an Error parameter to vmstate load member Laurent Vivier
@ 2026-07-29 23:18 ` Laurent Vivier
  2026-07-30  6:58   ` Thomas Huth
  2026-07-29 23:19 ` [PATCH v3 3/7] virtio: do not crash QEMU on migration errors Laurent Vivier
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier, qemu-stable, Daniel P. Berrangé, Thomas Huth

The migration restore path reads nr_active_ports from the incoming
stream and passes it directly to fetch_active_ports_list(), which
uses it to size a heap allocation. A crafted migration stream can set
this field to a very large value, causing QEMU to attempt a
multi-gigabyte allocation and abort.

Fix this by checking nr_active_ports against the configured
max_virtserial_ports before calling fetch_active_ports_list().

Cc: qemu-stable@nongnu.org
Fixes: 6663a1956eb6 ("virtio-serial-bus: Maintain guest and host port open/close state")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3801
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
---

Notes:
    v2: add error_setg() call with descriptive error message

 hw/char/virtio-serial-bus.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 87bfe51b6e93..4a5507e2f755 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -803,6 +803,12 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
 
     qemu_get_be32s(f, &nr_active_ports);
 
+    if (nr_active_ports > max_nr_ports) {
+        error_setg(errp, "Invalid number of active ports %u > %u", nr_active_ports,
+                   max_nr_ports);
+        return -EINVAL;
+    }
+
     if (nr_active_ports) {
         Error *local_err = NULL;
 
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 3/7] virtio: do not crash QEMU on migration errors
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
  2026-07-29 23:18 ` [PATCH v3 1/7] VirtioDeviceClass: Add an Error parameter to vmstate load member Laurent Vivier
  2026-07-29 23:18 ` [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream Laurent Vivier
@ 2026-07-29 23:19 ` Laurent Vivier
  2026-07-29 23:19 ` [PATCH v3 4/7] hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state Laurent Vivier
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier

From: "Michael S. Tsirkin" <mst@redhat.com>

Currently virtio-blk, virtio-serial and virtio-scsi all crash on invalid
migration streams because qemu_get_virtqueue_element has no way to
detect and report mapping failures.

Not nice.

Let's propagate mapping errors through qemu_get_virtqueue_element and
fail migration instead.

Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3888
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
[lvivier: use errp rather than qemu_file_set_error() to report error]
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: new patch from Michael S. Tsirkin
        https://lore.kernel.org/qemu-devel/a0afc9ea82d68f3447c9f58d89c9b46fa074d6c3.1784898250.git.mst@redhat.com/
        Modified to use errp parameter with error_setg() instead of
        qemu_file_set_error() to report errors in virtio-blk,
        virtio-serial, and virtio-scsi

 hw/block/virtio-blk.c       |  4 ++++
 hw/char/virtio-serial-bus.c |  4 ++++
 hw/scsi/virtio-scsi.c       |  4 ++++
 hw/virtio/virtio.c          | 48 +++++++++++++++++++++++++++----------
 include/hw/virtio/virtio.h  |  2 +-
 5 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 352b897c4ae5..ccd6ad404ca0 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1385,6 +1385,10 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
         }
 
         req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOBlockReq));
+        if (!req) {
+            error_setg(errp, "Failed to restore virtio-blk request");
+            return -EINVAL;
+        }
         virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req);
 
         WITH_QEMU_LOCK_GUARD(&s->rq_lock) {
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 4a5507e2f755..ecb7c5c7e507 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -764,6 +764,10 @@ static int fetch_active_ports_list(QEMUFile *f, VirtIOSerial *s,
 
             port->elem =
                 qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement));
+            if (!port->elem) {
+                error_setg(errp, "Failed to restore virtio-serial element");
+                return -EINVAL;
+            }
 
             /*
              *  Port was throttled on source machine.  Let's
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 54667dc4f51d..10eee3a4cff8 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -275,6 +275,10 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq,
     assert(n < vs->conf.num_queues);
     req = qemu_get_virtqueue_element(vdev, f,
                                      sizeof(VirtIOSCSIReq) + vs->cdb_size);
+    if (!req) {
+        error_setg(errp, "Failed to restore virtio-scsi request");
+        return NULL;
+    }
     virtio_scsi_init_req(s, vs->cmd_vqs[n], req);
 
     if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 64d780b1336e..02f75a5e281b 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1680,36 +1680,55 @@ static void virtqueue_undo_map_desc(AddressSpace *as,
     }
 }
 
-static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg,
+static bool virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg,
                                 hwaddr *addr, unsigned int num_sg,
                                 bool is_write)
 {
     unsigned int i;
     hwaddr len;
+    DMADirection dir = is_write ? DMA_DIRECTION_FROM_DEVICE :
+                                 DMA_DIRECTION_TO_DEVICE;
 
     for (i = 0; i < num_sg; i++) {
         len = sg[i].iov_len;
-        sg[i].iov_base = dma_memory_map(vdev->dma_as,
-                                        addr[i], &len, is_write ?
-                                        DMA_DIRECTION_FROM_DEVICE :
-                                        DMA_DIRECTION_TO_DEVICE,
-                                        MEMTXATTRS_UNSPECIFIED);
+        sg[i].iov_base = dma_memory_map(vdev->dma_as, addr[i], &len,
+                                        dir, MEMTXATTRS_UNSPECIFIED);
         if (!sg[i].iov_base) {
             error_report("virtio: error trying to map MMIO memory");
-            exit(1);
+            goto err_undo_map;
         }
         if (len != sg[i].iov_len) {
             error_report("virtio: unexpected memory split");
-            exit(1);
+            dma_memory_unmap(vdev->dma_as, sg[i].iov_base, len, dir, 0);
+            goto err_undo_map;
         }
     }
+    return true;
+
+err_undo_map:
+    while (i-- > 0) {
+        dma_memory_unmap(vdev->dma_as, sg[i].iov_base, sg[i].iov_len,
+                         dir, 0);
+    }
+    return false;
 }
 
-void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem)
+bool virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem)
 {
-    virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, elem->in_num, true);
-    virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, elem->out_num,
-                                                                        false);
+    if (!virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr,
+                             elem->in_num, true)) {
+        return false;
+    }
+    if (!virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr,
+                             elem->out_num, false)) {
+        for (unsigned int i = 0; i < elem->in_num; i++) {
+            dma_memory_unmap(vdev->dma_as, elem->in_sg[i].iov_base,
+                             elem->in_sg[i].iov_len,
+                             DMA_DIRECTION_FROM_DEVICE, 0);
+        }
+        return false;
+    }
+    return true;
 }
 
 static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num)
@@ -2206,7 +2225,10 @@ void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz)
         qemu_get_be32s(f, &elem->ndescs);
     }
 
-    virtqueue_map(vdev, elem);
+    if (!virtqueue_map(vdev, elem)) {
+        g_free(elem);
+        return NULL;
+    }
     return elem;
 }
 
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index bcb03154e243..d0c63500a58d 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -320,7 +320,7 @@ bool virtqueue_rewind(VirtQueue *vq, unsigned int num);
 void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
                     unsigned int len, unsigned int idx);
 
-void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem);
+bool virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem);
 void *virtqueue_pop(VirtQueue *vq, size_t sz);
 unsigned int virtqueue_drop_all(VirtQueue *vq);
 void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz);
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 4/7] hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (2 preceding siblings ...)
  2026-07-29 23:19 ` [PATCH v3 3/7] virtio: do not crash QEMU on migration errors Laurent Vivier
@ 2026-07-29 23:19 ` Laurent Vivier
  2026-07-29 23:19 ` [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors Laurent Vivier
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier, qemu-stable

qemu_get_virtqueue_element() uses assert() to check that the in_num
and out_num fields deserialized from the migration stream do not
exceed VIRTQUEUE_MAX_SIZE. A crafted migration stream can set these
fields to invalid values, hitting the assertion and aborting the
destination QEMU process.

Replace the assertions with a bounds check that returns NULL on
failure.

Cc: qemu-stable@nongnu.org
Fixes: 6bdc21c050a2 ("virtio: fix up max size checks")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3802
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: remove caller updates (now handled by patch 3 from Michael)

 hw/virtio/virtio.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 02f75a5e281b..c37ef01bf513 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2190,13 +2190,10 @@ void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz)
 
     qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld));
 
-    /* TODO: teach all callers that this can fail, and return failure instead
-     * of asserting here.
-     * This is just one thing (there are probably more) that must be
-     * fixed before we can allow NDEBUG compilation.
-     */
-    assert(ARRAY_SIZE(data.in_addr) >= data.in_num);
-    assert(ARRAY_SIZE(data.out_addr) >= data.out_num);
+    if (data.in_num > ARRAY_SIZE(data.in_addr) ||
+        data.out_num > ARRAY_SIZE(data.out_addr)) {
+        return NULL;
+    }
 
     elem = virtqueue_alloc_element(sz, data.out_num, data.in_num);
     elem->index = data.index;
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (3 preceding siblings ...)
  2026-07-29 23:19 ` [PATCH v3 4/7] hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state Laurent Vivier
@ 2026-07-29 23:19 ` Laurent Vivier
  2026-07-30 14:01   ` Laurent Vivier
  2026-07-29 23:19 ` [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream Laurent Vivier
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier

From: "Michael S. Tsirkin" <mst@redhat.com>

Currently mptsas asserts on invalid sg list count in the migration
stream.

Fail migration gracefully instead.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
[lvivier: use errp rather than qemu_file_set_error() to report error]
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: new patch from Michael S. Tsirkin
        https://lore.kernel.org/qemu-devel/258d84f5af807e0103bf6f59165dd7450918e58c.1784898250.git.mst@redhat.com/
        Modified to use errp parameter with error_setg() instead of
        qemu_file_set_error() to report error

 hw/scsi/mptsas.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 45638af0afc3..34ceb6b5d25e 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -36,6 +36,8 @@
 #include "qapi/error.h"
 #include "mptsas.h"
 #include "migration/qemu-file-types.h"
+#include "migration/qemu-file.h"
+#include "qemu/error-report.h"
 #include "migration/vmstate.h"
 #include "mpi.h"
 
@@ -1242,12 +1244,12 @@ static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
     qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
 
     n = qemu_get_be32(f);
-    /* TODO: add a way for SCSIBusInfo's load_request to fail,
-     * and fail migration instead of asserting here.
-     * This is just one thing (there are probably more) that must be
-     * fixed before we can allow NDEBUG compilation.
-     */
-    assert(n >= 0);
+    if (n < 0) {
+        error_setg(errp, "mptsas: invalid sg list count %d in migration stream",
+                   n);
+        g_free(req);
+        return NULL;
+    }
 
     pci_dma_sglist_init(&req->qsg, pci, n);
     for (i = 0; i < n; i++) {
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (4 preceding siblings ...)
  2026-07-29 23:19 ` [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors Laurent Vivier
@ 2026-07-29 23:19 ` Laurent Vivier
  2026-07-30 13:35   ` Stefan Hajnoczi
  2026-07-29 23:19 ` [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors Laurent Vivier
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier, qemu-stable

virtio_scsi_load_request() uses assert() and exit(1) for conditions
that can be triggered by a crafted migration stream: an out-of-range
queue index, a malformed SCSI request, or a command mode mismatch.

Replace these with proper error returns so the migration fails
gracefully instead of aborting the destination QEMU process.

Cc: qemu-stable@nongnu.org
Fixes: 5db1764cc1f6 ("virtio-scsi: add migration support")
Fixes: d2ad7dd46e72 ("virtio-scsi: add multiqueue capability")
Fixes: 36b15c79aa1b ("virtio-scsi: start preparing for any_layout")
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: add error_setg() calls with descriptive error messages for
        all three error paths

 hw/scsi/virtio-scsi.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 10eee3a4cff8..8814b246048f 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -272,7 +272,12 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq,
     uint32_t n;
 
     qemu_get_be32s(f, &n);
-    assert(n < vs->conf.num_queues);
+    if (n >= vs->conf.num_queues) {
+        error_setg(errp, "Invalid queues number %u > %u", n,
+                   vs->conf.num_queues);
+        return NULL;
+    }
+
     req = qemu_get_virtqueue_element(vdev, f,
                                      sizeof(VirtIOSCSIReq) + vs->cdb_size);
     if (!req) {
@@ -283,14 +288,20 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq,
 
     if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
                               sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
-        error_report("invalid SCSI request migration data");
-        exit(1);
+        error_setg(errp, "invalid SCSI request migration data");
+        virtio_scsi_free_req(req);
+        return NULL;
     }
 
     scsi_req_ref(sreq);
     req->sreq = sreq;
-    if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
-        assert(req->sreq->cmd.mode == req->mode);
+    if (req->sreq->cmd.mode != SCSI_XFER_NONE &&
+        req->sreq->cmd.mode != req->mode) {
+        error_setg(errp, "Invalid SCSI request mode %u",
+                   req->sreq->cmd.mode);
+        scsi_req_unref(sreq);
+        virtio_scsi_free_req(req);
+        return NULL;
     }
     return req;
 }
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (5 preceding siblings ...)
  2026-07-29 23:19 ` [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream Laurent Vivier
@ 2026-07-29 23:19 ` Laurent Vivier
  2026-07-30 13:36   ` Stefan Hajnoczi
  2026-07-30  9:20 ` [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Michael S. Tsirkin
  2026-07-30 10:49 ` Michael S. Tsirkin
  8 siblings, 1 reply; 14+ messages in thread
From: Laurent Vivier @ 2026-07-29 23:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	Laurent Vivier

Currently vscsi asserts on invalid SCSI requests.

Fail migration gracefully instead.

Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---

Notes:
    v2: New patch to manage vscsi

 hw/scsi/spapr_vscsi.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 7768ec0bdd49..b6a0411e9c44 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -650,9 +650,17 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
     int rc;
     Error *local_err = NULL;
 
-    assert(sreq->tag < VSCSI_REQ_LIMIT);
+    if (sreq->tag >= VSCSI_REQ_LIMIT) {
+        error_setg(errp, "VSCSI: request tag#%u out of range (max %d)",
+                   sreq->tag, VSCSI_REQ_LIMIT);
+        return NULL;
+    }
+
     req = &s->reqs[sreq->tag];
-    assert(!req->active);
+    if (req->active) {
+        error_setg(errp, "VSCSI: request tag#%u already active", sreq->tag);
+        return NULL;
+    }
 
     memset(req, 0, sizeof(*req));
     rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1, &local_err);
@@ -662,7 +670,11 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
                                 sreq->tag);
         return NULL;
     }
-    assert(req->active);
+    if (!req->active) {
+        error_setg(errp, "VSCSI: request tag#%u not active after load",
+                   sreq->tag);
+        return NULL;
+    }
 
     req->sreq = scsi_req_ref(sreq);
 
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream
  2026-07-29 23:18 ` [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream Laurent Vivier
@ 2026-07-30  6:58   ` Thomas Huth
  0 siblings, 0 replies; 14+ messages in thread
From: Thomas Huth @ 2026-07-30  6:58 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi,
	qemu-stable, Daniel P. Berrangé

On 30/07/2026 01.18, Laurent Vivier wrote:
> The migration restore path reads nr_active_ports from the incoming
> stream and passes it directly to fetch_active_ports_list(), which
> uses it to size a heap allocation. A crafted migration stream can set
> this field to a very large value, causing QEMU to attempt a
> multi-gigabyte allocation and abort.
> 
> Fix this by checking nr_active_ports against the configured
> max_virtserial_ports before calling fetch_active_ports_list().
> 
> Cc: qemu-stable@nongnu.org
> Fixes: 6663a1956eb6 ("virtio-serial-bus: Maintain guest and host port open/close state")
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3801
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Reviewed-by: Thomas Huth <thuth@redhat.com>
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> 
> Notes:
>      v2: add error_setg() call with descriptive error message
> 
>   hw/char/virtio-serial-bus.c | 6 ++++++
>   1 file changed, 6 insertions(+)
> 
> diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
> index 87bfe51b6e93..4a5507e2f755 100644
> --- a/hw/char/virtio-serial-bus.c
> +++ b/hw/char/virtio-serial-bus.c
> @@ -803,6 +803,12 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
>   
>       qemu_get_be32s(f, &nr_active_ports);
>   
> +    if (nr_active_ports > max_nr_ports) {
> +        error_setg(errp, "Invalid number of active ports %u > %u", nr_active_ports,
> +                   max_nr_ports);
> +        return -EINVAL;
> +    }
> +
>       if (nr_active_ports) {
>           Error *local_err = NULL;
>   

Reviewed-by: Thomas Huth <thuth@redhat.com>



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 0/7] Harden virtio migration load paths against crafted streams
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (6 preceding siblings ...)
  2026-07-29 23:19 ` [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors Laurent Vivier
@ 2026-07-30  9:20 ` Michael S. Tsirkin
  2026-07-30 10:49 ` Michael S. Tsirkin
  8 siblings, 0 replies; 14+ messages in thread
From: Michael S. Tsirkin @ 2026-07-30  9:20 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: qemu-devel, Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi

On Thu, Jul 30, 2026 at 01:18:57AM +0200, Laurent Vivier wrote:
> A crafted migration stream can crash the destination QEMU process
> through unvalidated fields in the virtio device state: an unbounded
> allocation in virtio-serial, reachable assertions in the shared
> virtqueue element deserializer, assert()/exit(1) calls in
> virtio-scsi and spapr-vscsi request loading, and missing error
> propagation through the SCSI and virtio device load paths.
> 
> These are hardening fixes: the destination QEMU is in a paused
> pre-start state and the source VM is unaffected by a failed migration.
> 
> Patch 1 adds an Error** parameter to the virtio and SCSI load_request
> callbacks, allowing proper error propagation instead of error_report()
> or silent failures. This provides the infrastructure for the remaining
> patches.
> 
> Patch 2 validates the virtio-serial nr_active_ports count against the
> configured maximum before allocating the post-load array.
> 
> Patches 3 and 5 are from Michael S. Tsirkin, modified to use the new
> Error** parameter:
> 
> Patch 3 makes virtqueue_map() return bool instead of calling exit(1)
> on mapping failures, and propagates errors through
> qemu_get_virtqueue_element() to virtio-blk, virtio-serial, and
> virtio-scsi.
> 
> Patch 4 replaces the assertions in qemu_get_virtqueue_element() with
> a bounds check returning NULL on invalid in_num/out_num counts.
> 
> Patch 5 replaces the assertion in mptsas_load_request() with proper
> error handling.
> 
> Patch 6 replaces the remaining assert() and exit(1) calls in
> virtio_scsi_load_request() with proper error returns.
> 
> Patch 7 replaces the assert() calls in vscsi_load_request() with
> proper error returns and propagation.


Reviewed-by: Michael S. Tsirkin <mst@redhat.com>

> v2:
> - New patch 1 to add Error** parameter to load_request callbacks
> - Patches 2, 4, 6, 7: add error_setg() calls with descriptive messages
> - New patches 3 and 5 from Michael S. Tsirkin (modified to use errp)
> - Patch 4: remove caller updates (now handled by patch 3)
> - New patch 7: harden spapr_vscsi load_request
> 
> Tested with migration round-trips for virtio-serial (0 to 511 ports),
> virtio-blk (1-2 disks), virtio-scsi (1-2 disks), mptsas1068
> (with and without scsi-hd), and spapr-vscsi, plus the original PoC
> reproducers for issues #3801, #3802, and #3888. ppc64 qtests pass.
> 
> Laurent Vivier (5):
>   VirtioDeviceClass: Add an Error parameter to vmstate load member
>   hw/char/virtio-serial-bus: validate nr_active_ports from migration stream
>   hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state
>   hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream
>   hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
> 
> Michael S. Tsirkin (2):
>   virtio: do not crash QEMU on migration errors
>   mptsas: do not crash QEMU on migration errors
> 
>  hw/block/virtio-blk.c       | 16 ++++++--
>  hw/char/virtio-serial-bus.c | 33 ++++++++++----
>  hw/scsi/esp.c               |  2 +-
>  hw/scsi/mptsas.c            | 18 +++++---
>  hw/scsi/scsi-bus.c          | 25 +++++++++--
>  hw/scsi/scsi-disk.c         | 10 +++--
>  hw/scsi/scsi-generic.c      |  3 +-
>  hw/scsi/spapr_vscsi.c       | 18 +++++++--
>  hw/scsi/virtio-scsi.c       | 27 ++++++++++--
>  hw/usb/dev-storage.c        |  2 +-
>  hw/virtio/virtio.c          | 82 ++++++++++++++++++++++++++-----------
>  include/hw/scsi/scsi.h      |  4 +-
>  include/hw/usb/msd.h        |  2 +-
>  include/hw/virtio/virtio.h  |  4 +-
>  14 files changed, 182 insertions(+), 62 deletions(-)
> 
> Laurent Vivier (5):
>   VirtioDeviceClass: Add an Error parameter to vmstate load member
>   hw/char/virtio-serial-bus: validate nr_active_ports from migration
>     stream
>   hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid
>     state
>   hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid
>     stream
>   hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
> 
> Michael S. Tsirkin (2):
>   virtio: do not crash QEMU on migration errors
>   mptsas: do not crash QEMU on migration errors
> 
>  hw/block/virtio-blk.c       | 11 +++++--
>  hw/char/virtio-serial-bus.c | 30 +++++++++++------
>  hw/scsi/esp.c               |  2 +-
>  hw/scsi/mptsas.c            | 16 ++++++----
>  hw/scsi/scsi-bus.c          | 18 +++++++++--
>  hw/scsi/scsi-disk.c         |  7 ++--
>  hw/scsi/scsi-generic.c      |  3 +-
>  hw/scsi/spapr_vscsi.c       | 25 +++++++++++----
>  hw/scsi/virtio-scsi.c       | 28 ++++++++++++----
>  hw/usb/dev-storage.c        |  2 +-
>  hw/virtio/virtio.c          | 64 ++++++++++++++++++++++++-------------
>  include/hw/scsi/scsi.h      |  4 +--
>  include/hw/usb/msd.h        |  2 +-
>  include/hw/virtio/virtio.h  |  4 +--
>  14 files changed, 150 insertions(+), 66 deletions(-)
> 
> -- 
> 2.54.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 0/7] Harden virtio migration load paths against crafted streams
  2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
                   ` (7 preceding siblings ...)
  2026-07-30  9:20 ` [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Michael S. Tsirkin
@ 2026-07-30 10:49 ` Michael S. Tsirkin
  8 siblings, 0 replies; 14+ messages in thread
From: Michael S. Tsirkin @ 2026-07-30 10:49 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: qemu-devel, Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi

On Thu, Jul 30, 2026 at 01:18:57AM +0200, Laurent Vivier wrote:
> A crafted migration stream can crash the destination QEMU process
> through unvalidated fields in the virtio device state: an unbounded
> allocation in virtio-serial, reachable assertions in the shared
> virtqueue element deserializer, assert()/exit(1) calls in
> virtio-scsi and spapr-vscsi request loading, and missing error
> propagation through the SCSI and virtio device load paths.
> 
> These are hardening fixes: the destination QEMU is in a paused
> pre-start state and the source VM is unaffected by a failed migration.
> 
> Patch 1 adds an Error** parameter to the virtio and SCSI load_request
> callbacks, allowing proper error propagation instead of error_report()
> or silent failures. This provides the infrastructure for the remaining
> patches.
> 
> Patch 2 validates the virtio-serial nr_active_ports count against the
> configured maximum before allocating the post-load array.
> 
> Patches 3 and 5 are from Michael S. Tsirkin, modified to use the new
> Error** parameter:
> 
> Patch 3 makes virtqueue_map() return bool instead of calling exit(1)
> on mapping failures, and propagates errors through
> qemu_get_virtqueue_element() to virtio-blk, virtio-serial, and
> virtio-scsi.
> 
> Patch 4 replaces the assertions in qemu_get_virtqueue_element() with
> a bounds check returning NULL on invalid in_num/out_num counts.
> 
> Patch 5 replaces the assertion in mptsas_load_request() with proper
> error handling.
> 
> Patch 6 replaces the remaining assert() and exit(1) calls in
> virtio_scsi_load_request() with proper error returns.
> 
> Patch 7 replaces the assert() calls in vscsi_load_request() with
> proper error returns and propagation.


who's merging this btw? me?

> v2:
> - New patch 1 to add Error** parameter to load_request callbacks
> - Patches 2, 4, 6, 7: add error_setg() calls with descriptive messages
> - New patches 3 and 5 from Michael S. Tsirkin (modified to use errp)
> - Patch 4: remove caller updates (now handled by patch 3)
> - New patch 7: harden spapr_vscsi load_request
> 
> Tested with migration round-trips for virtio-serial (0 to 511 ports),
> virtio-blk (1-2 disks), virtio-scsi (1-2 disks), mptsas1068
> (with and without scsi-hd), and spapr-vscsi, plus the original PoC
> reproducers for issues #3801, #3802, and #3888. ppc64 qtests pass.
> 
> Laurent Vivier (5):
>   VirtioDeviceClass: Add an Error parameter to vmstate load member
>   hw/char/virtio-serial-bus: validate nr_active_ports from migration stream
>   hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state
>   hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream
>   hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
> 
> Michael S. Tsirkin (2):
>   virtio: do not crash QEMU on migration errors
>   mptsas: do not crash QEMU on migration errors
> 
>  hw/block/virtio-blk.c       | 16 ++++++--
>  hw/char/virtio-serial-bus.c | 33 ++++++++++----
>  hw/scsi/esp.c               |  2 +-
>  hw/scsi/mptsas.c            | 18 +++++---
>  hw/scsi/scsi-bus.c          | 25 +++++++++--
>  hw/scsi/scsi-disk.c         | 10 +++--
>  hw/scsi/scsi-generic.c      |  3 +-
>  hw/scsi/spapr_vscsi.c       | 18 +++++++--
>  hw/scsi/virtio-scsi.c       | 27 ++++++++++--
>  hw/usb/dev-storage.c        |  2 +-
>  hw/virtio/virtio.c          | 82 ++++++++++++++++++++++++++-----------
>  include/hw/scsi/scsi.h      |  4 +-
>  include/hw/usb/msd.h        |  2 +-
>  include/hw/virtio/virtio.h  |  4 +-
>  14 files changed, 182 insertions(+), 62 deletions(-)
> 
> Laurent Vivier (5):
>   VirtioDeviceClass: Add an Error parameter to vmstate load member
>   hw/char/virtio-serial-bus: validate nr_active_ports from migration
>     stream
>   hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid
>     state
>   hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid
>     stream
>   hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
> 
> Michael S. Tsirkin (2):
>   virtio: do not crash QEMU on migration errors
>   mptsas: do not crash QEMU on migration errors
> 
>  hw/block/virtio-blk.c       | 11 +++++--
>  hw/char/virtio-serial-bus.c | 30 +++++++++++------
>  hw/scsi/esp.c               |  2 +-
>  hw/scsi/mptsas.c            | 16 ++++++----
>  hw/scsi/scsi-bus.c          | 18 +++++++++--
>  hw/scsi/scsi-disk.c         |  7 ++--
>  hw/scsi/scsi-generic.c      |  3 +-
>  hw/scsi/spapr_vscsi.c       | 25 +++++++++++----
>  hw/scsi/virtio-scsi.c       | 28 ++++++++++++----
>  hw/usb/dev-storage.c        |  2 +-
>  hw/virtio/virtio.c          | 64 ++++++++++++++++++++++++-------------
>  include/hw/scsi/scsi.h      |  4 +--
>  include/hw/usb/msd.h        |  2 +-
>  include/hw/virtio/virtio.h  |  4 +--
>  14 files changed, 150 insertions(+), 66 deletions(-)
> 
> -- 
> 2.54.0



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream
  2026-07-29 23:19 ` [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream Laurent Vivier
@ 2026-07-30 13:35   ` Stefan Hajnoczi
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Hajnoczi @ 2026-07-30 13:35 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: qemu-devel, Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, qemu-stable

[-- Attachment #1: Type: text/plain, Size: 987 bytes --]

On Thu, Jul 30, 2026 at 01:19:03AM +0200, Laurent Vivier wrote:
> virtio_scsi_load_request() uses assert() and exit(1) for conditions
> that can be triggered by a crafted migration stream: an out-of-range
> queue index, a malformed SCSI request, or a command mode mismatch.
> 
> Replace these with proper error returns so the migration fails
> gracefully instead of aborting the destination QEMU process.
> 
> Cc: qemu-stable@nongnu.org
> Fixes: 5db1764cc1f6 ("virtio-scsi: add migration support")
> Fixes: d2ad7dd46e72 ("virtio-scsi: add multiqueue capability")
> Fixes: 36b15c79aa1b ("virtio-scsi: start preparing for any_layout")
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> ---
> 
> Notes:
>     v2: add error_setg() calls with descriptive error messages for
>         all three error paths
> 
>  hw/scsi/virtio-scsi.c | 21 ++++++++++++++++-----
>  1 file changed, 16 insertions(+), 5 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors
  2026-07-29 23:19 ` [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors Laurent Vivier
@ 2026-07-30 13:36   ` Stefan Hajnoczi
  0 siblings, 0 replies; 14+ messages in thread
From: Stefan Hajnoczi @ 2026-07-30 13:36 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: qemu-devel, Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block

[-- Attachment #1: Type: text/plain, Size: 437 bytes --]

On Thu, Jul 30, 2026 at 01:19:04AM +0200, Laurent Vivier wrote:
> Currently vscsi asserts on invalid SCSI requests.
> 
> Fail migration gracefully instead.
> 
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> ---
> 
> Notes:
>     v2: New patch to manage vscsi
> 
>  hw/scsi/spapr_vscsi.c | 18 +++++++++++++++---
>  1 file changed, 15 insertions(+), 3 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors
  2026-07-29 23:19 ` [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors Laurent Vivier
@ 2026-07-30 14:01   ` Laurent Vivier
  0 siblings, 0 replies; 14+ messages in thread
From: Laurent Vivier @ 2026-07-30 14:01 UTC (permalink / raw)
  To: qemu-devel
  Cc: Amit Shah, qemu-ppc, Harsh Prateek Bora, Hanna Reitz,
	Nicholas Piggin, Michael S. Tsirkin, Kevin Wolf, Paolo Bonzini,
	Marc-André Lureau, Fam Zheng, qemu-block, Stefan Hajnoczi

On 7/30/26 01:19, Laurent Vivier wrote:
> From: "Michael S. Tsirkin" <mst@redhat.com>
> 
> Currently mptsas asserts on invalid sg list count in the migration
> stream.
> 
> Fail migration gracefully instead.
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> [lvivier: use errp rather than qemu_file_set_error() to report error]
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> ---
> 
> Notes:
>      v2: new patch from Michael S. Tsirkin
>          https://lore.kernel.org/qemu-devel/258d84f5af807e0103bf6f59165dd7450918e58c.1784898250.git.mst@redhat.com/
>          Modified to use errp parameter with error_setg() instead of
>          qemu_file_set_error() to report error
> 
>   hw/scsi/mptsas.c | 14 ++++++++------
>   1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
> index 45638af0afc3..34ceb6b5d25e 100644
> --- a/hw/scsi/mptsas.c
> +++ b/hw/scsi/mptsas.c
> @@ -36,6 +36,8 @@
>   #include "qapi/error.h"
>   #include "mptsas.h"
>   #include "migration/qemu-file-types.h"
> +#include "migration/qemu-file.h"

When I removed qemu_file_set_error() I forgot to remove this include.
Perhaps it can be removed on merge? Otherwise I will send a v3.

Thanks,
Laurent

> +#include "qemu/error-report.h"
>   #include "migration/vmstate.h"
>   #include "mpi.h"
>   
> @@ -1242,12 +1244,12 @@ static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq, Error **errp)
>       qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
>   
>       n = qemu_get_be32(f);
> -    /* TODO: add a way for SCSIBusInfo's load_request to fail,
> -     * and fail migration instead of asserting here.
> -     * This is just one thing (there are probably more) that must be
> -     * fixed before we can allow NDEBUG compilation.
> -     */
> -    assert(n >= 0);
> +    if (n < 0) {
> +        error_setg(errp, "mptsas: invalid sg list count %d in migration stream",
> +                   n);
> +        g_free(req);
> +        return NULL;
> +    }
>   
>       pci_dma_sglist_init(&req->qsg, pci, n);
>       for (i = 0; i < n; i++) {



^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-07-30 14:02 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 23:18 [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Laurent Vivier
2026-07-29 23:18 ` [PATCH v3 1/7] VirtioDeviceClass: Add an Error parameter to vmstate load member Laurent Vivier
2026-07-29 23:18 ` [PATCH v3 2/7] hw/char/virtio-serial-bus: validate nr_active_ports from migration stream Laurent Vivier
2026-07-30  6:58   ` Thomas Huth
2026-07-29 23:19 ` [PATCH v3 3/7] virtio: do not crash QEMU on migration errors Laurent Vivier
2026-07-29 23:19 ` [PATCH v3 4/7] hw/virtio: return NULL from qemu_get_virtqueue_element() on invalid state Laurent Vivier
2026-07-29 23:19 ` [PATCH v3 5/7] mptsas: do not crash QEMU on migration errors Laurent Vivier
2026-07-30 14:01   ` Laurent Vivier
2026-07-29 23:19 ` [PATCH v3 6/7] hw/scsi/virtio-scsi: harden virtio_scsi_load_request() against invalid stream Laurent Vivier
2026-07-30 13:35   ` Stefan Hajnoczi
2026-07-29 23:19 ` [PATCH v3 7/7] hw/scsi/spapr_vscsi: do not crash QEMU on migration errors Laurent Vivier
2026-07-30 13:36   ` Stefan Hajnoczi
2026-07-30  9:20 ` [PATCH v3 0/7] Harden virtio migration load paths against crafted streams Michael S. Tsirkin
2026-07-30 10:49 ` Michael S. Tsirkin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.