hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
From: Marc-André Lureau <marcandre.lureau@redhat.com>
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>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
v2:
- moved validation to attach_backing()
- added validation during blob_load()
- accept nr_entries == 0 during create_blob()
---
hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index eac039c3c366..211a529ed720 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);
}
@@ -992,6 +1003,16 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
return;
}
+
+ if (res->blob_size &&
+ 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;
+ }
}
static void
@@ -1489,6 +1510,13 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size,
res->iov[i].iov_len = qemu_get_be32(f);
}
+ if (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;
--
2.55.0
On 2026/07/25 20:13, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> 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>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> v2:
> - moved validation to attach_backing()
> - added validation during blob_load()
> - accept nr_entries == 0 during create_blob()
> ---
> hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> index eac039c3c366..211a529ed720 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);
> }
> @@ -992,6 +1003,16 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
> cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
> return;
> }
> +
> + if (res->blob_size &&
This check of res->blob_size is redundant.
> + 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;
> + }
> }
>
> static void
> @@ -1489,6 +1510,13 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size,
> res->iov[i].iov_len = qemu_get_be32(f);
> }
>
> + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) {
This check should be applied only when res->iov_cnt > 0, matching the
create path.
Regards,
Akihiko Odaki
> + 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;
© 2016 - 2026 Red Hat, Inc.