Commit 241095547a for qemu.org

commit 241095547a5d87ad6fa68cd674fe524e6596b958
Author: Marc-André Lureau <marcandre.lureau@redhat.com>
Date:   Wed Jul 29 20:14:31 2026 +0400

    hw/display/virtio-gpu: validate blob iov size

    virtio_gpu_resource_create_blob() stores the guest-controlled blob_size
    without checking it against the total size of the iov backing entries.
    Since both values are independently guest-controlled, a malicious guest
    can set blob_size much larger than the actual iov backing. Subsequent
    SET_SCANOUT_BLOB checks bounds against the inflated blob_size, allowing
    a pixman surface to be created over the undersized buffer. Any display
    refresh then reads past the actual allocation, potentially crashing
    QEMU or leaking host memory contents depending on the backing type.

    Validate that the iov backing is at least as large as the declared
    blob_size in create_blob (when nr_entries > 0, since the spec permits
    deferred backing), attach_backing (when attaching to a blob resource),
    and the blob migration load path.

    Fixes: CVE-2026-66021
    Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob")
    Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3945
    Reported-by: "sundayjiang(蒋浩天)" <sundayjiang@tencent.com>
    Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
    Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
    Message-ID: <20260729161431.1180691-1-marcandre.lureau@redhat.com>

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 4d46a4eb10..0206910cc3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -372,6 +372,17 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g,
         return;
     }

+    if (res->iov_cnt > 0 &&
+        iov_size(res->iov, res->iov_cnt) < res->blob_size) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: backing storage smaller than blob size\n",
+                      __func__);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        virtio_gpu_cleanup_mapping(g, res);
+        g_free(res);
+        return;
+    }
+
     virtio_gpu_init_udmabuf(res);
     QTAILQ_INSERT_HEAD(&g->reslist, res, next);
 }
@@ -993,6 +1004,15 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
         return;
     }

+    if (iov_size(res->iov, res->iov_cnt) < res->blob_size) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: backing storage smaller than blob size\n",
+                      __func__);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        virtio_gpu_cleanup_mapping(g, res);
+        return;
+    }
+
     if (!res->image) {
         virtio_gpu_init_udmabuf(res);
     }
@@ -1493,6 +1513,14 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size,
             res->iov[i].iov_len = qemu_get_be32(f);
         }

+        if (res->iov_cnt > 0 &&
+            iov_size(res->iov, res->iov_cnt) < res->blob_size) {
+            g_free(res->addrs);
+            g_free(res->iov);
+            g_free(res);
+            return -EINVAL;
+        }
+
         if (!virtio_gpu_load_restore_mapping(g, res)) {
             g_free(res);
             return -EINVAL;