@samitouri / QOSamiQemu / commits / a5cff318f0

virtio-mmio: fix QUEUE_NUM_MAX

virtio-mmio reports VIRTQUEUE_MAX_SIZE (1024) as QUEUE_NUM_MAX for every queue, regardless of the size the device passes to virtio_add_queue(). This works by accident because QEMU mostly does not care about the ring size - the guest is the one allocating memory here. But this changes with in-order vqs where qemu is the one allocating resources. Now, specifying a larger vq than allocated causes an OOB memory access. To fix: - for new machine types, report the actual max queue size to guest - for old machine types, use a compat property to allocate 1k sized queues Fixes: 525d82e323 ("virtio: fix queue size validation against allocated maximum") Fixes: CVE-2026-50626 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3882 Cc: Peter Maydell <peter.maydell@linaro.org> Message-ID: <8715acbb9516e67e2a776cda6f9edf105343f788.1784930765.git.mst@redhat.com> Acked-by: Yonggang Luo <luoyonggang@gmail.com> Reported-by: Miku Hatsune <anznu1l@gmail.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

Michael S. Tsirkin committed Jul 24, 2026 at 16:09 UTC a5cff318f06cd06b37224e15ee74d64d1df8b12b
5 files changed +17 -4
hw/core/machine.c
+1
@@ -41,6 +41,7 @@
41 #include "hw/arm/smmuv3.h"
42
43 GlobalProperty hw_compat_11_0[] = {
44 + { "virtio-mmio", VIRTIO_QUEUE_SIZE_OVERRIDE, "1024" },
45 { "chardev-vc", "encoding", "cp437" },
46 { "tpm-crb", "cap-chunk", "off" },
47 { "tpm-crb", "x-allow-chunk-migration", "off" },
hw/virtio/virtio-mmio.c
+3 -4
@@ -172,10 +172,7 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
172 >> (32 * proxy->host_features_sel);
173 }
174 case VIRTIO_MMIO_QUEUE_NUM_MAX:
175 - if (!virtio_queue_get_num(vdev, vdev->queue_sel)) {
176 - return 0;
177 - }
178 - return VIRTQUEUE_MAX_SIZE;
175 + return virtio_queue_get_max_num(vdev, vdev->queue_sel);
176 case VIRTIO_MMIO_QUEUE_PFN:
177 if (!proxy->legacy) {
178 qemu_log_mask(LOG_GUEST_ERROR,
@@ -738,6 +735,8 @@ static const Property virtio_mmio_properties[] = {
735 DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true),
736 DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags,
737 VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true),
738 + DEFINE_PROP_UINT16(VIRTIO_QUEUE_SIZE_OVERRIDE, VirtIOMMIOProxy,
739 + override_queue_size, 0),
740 };
741
742 static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
hw/virtio/virtio.c
+11
@@ -2568,6 +2568,17 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
2568 if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE)
2569 abort();
2570
2571 + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
2572 + if (qbus && qbus->parent &&
2573 + object_property_find(OBJECT(qbus->parent), VIRTIO_QUEUE_SIZE_OVERRIDE)) {
2574 + int override = object_property_get_int(OBJECT(qbus->parent),
2575 + VIRTIO_QUEUE_SIZE_OVERRIDE,
2576 + &error_abort);
2577 + if (override) {
2578 + queue_size = override;
2579 + }
2580 + }
2581 +
2582 vdev->vq[i].vring.num = queue_size;
2583 vdev->vq[i].vring.num_default = queue_size;
2584 vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
include/hw/virtio/virtio-bus.h
+1
@@ -30,6 +30,7 @@
30 #include "qom/object.h"
31
32 #define TYPE_VIRTIO_BUS "virtio-bus"
33 +#define VIRTIO_QUEUE_SIZE_OVERRIDE "x-override-queue-size"
34 typedef struct VirtioBusClass VirtioBusClass;
35 typedef struct VirtioBusState VirtioBusState;
36 DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass,
include/hw/virtio/virtio-mmio.h
+1
@@ -69,6 +69,7 @@ struct VirtIOMMIOProxy {
69 /* Fields only used for non-legacy (v2) devices */
70 uint32_t guest_features[2];
71 VirtIOMMIOQueue vqs[VIRTIO_QUEUE_MAX];
72 + uint16_t override_queue_size;
73 };
74
75 #endif