@samitouri / QOSamiQemu / commits / 241095547a

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>

Marc-André Lureau committed Jul 29, 2026 at 20:14 UTC 241095547a5d87ad6fa68cd674fe524e6596b958
1 file changed +28
hw/display/virtio-gpu.c
+28
@@ -372,6 +372,17 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g,
372 return;
373 }
374
375 + if (res->iov_cnt > 0 &&
376 + iov_size(res->iov, res->iov_cnt) < res->blob_size) {
377 + qemu_log_mask(LOG_GUEST_ERROR,
378 + "%s: backing storage smaller than blob size\n",
379 + __func__);
380 + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
381 + virtio_gpu_cleanup_mapping(g, res);
382 + g_free(res);
383 + return;
384 + }
385 +
386 virtio_gpu_init_udmabuf(res);
387 QTAILQ_INSERT_HEAD(&g->reslist, res, next);
388 }
@@ -993,6 +1004,15 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
1004 return;
1005 }
1006
1007 + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) {
1008 + qemu_log_mask(LOG_GUEST_ERROR,
1009 + "%s: backing storage smaller than blob size\n",
1010 + __func__);
1011 + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
1012 + virtio_gpu_cleanup_mapping(g, res);
1013 + return;
1014 + }
1015 +
1016 if (!res->image) {
1017 virtio_gpu_init_udmabuf(res);
1018 }
@@ -1493,6 +1513,14 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size,
1513 res->iov[i].iov_len = qemu_get_be32(f);
1514 }
1515
1516 + if (res->iov_cnt > 0 &&
1517 + iov_size(res->iov, res->iov_cnt) < res->blob_size) {
1518 + g_free(res->addrs);
1519 + g_free(res->iov);
1520 + g_free(res);
1521 + return -EINVAL;
1522 + }
1523 +
1524 if (!virtio_gpu_load_restore_mapping(g, res)) {
1525 g_free(res);
1526 return -EINVAL;