hw/display/vhost-user-gpu.c | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
From: Marc-André Lureau <marcandre.lureau@redhat.com>
A malicious or buggy vhost-user-gpu backend can send messages with
undersized payloads, leading to out-of-bounds reads when the handler
accesses struct fields beyond the allocated buffer. However,
vhost-user-gpu is considered trusted by QEMU by design (it has access to
shared memory etc).
Add a centralized minimum payload size check in vhost_user_gpu_chr_read()
that rejects messages before dispatch, and a per-pixel bounds check in
the VHOST_USER_GPU_UPDATE handler to ensure the variable-length data
covers the declared width x height.
Fixes: 267f66465 ("hw/display: add vhost-user-vga & gpu-pci")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3866
Reported-by: Feifan Qian <bea1e@proton.me>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/display/vhost-user-gpu.c | 38 +++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index 57360898ca0..cd684d63639 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -119,6 +119,31 @@ static VhostUserGpuMsg m __attribute__ ((unused));
static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked);
+static size_t
+vhost_user_gpu_min_payload_size(VhostUserGpuRequest request)
+{
+ switch (request) {
+ case VHOST_USER_GPU_CURSOR_POS:
+ case VHOST_USER_GPU_CURSOR_POS_HIDE:
+ return sizeof(VhostUserGpuCursorPos);
+ case VHOST_USER_GPU_CURSOR_UPDATE:
+ return sizeof(VhostUserGpuCursorUpdate);
+ case VHOST_USER_GPU_GET_EDID:
+ return sizeof(VhostUserGpuEdidRequest);
+ case VHOST_USER_GPU_SCANOUT:
+ return sizeof(VhostUserGpuScanout);
+ case VHOST_USER_GPU_DMABUF_SCANOUT:
+ return sizeof(VhostUserGpuDMABUFScanout);
+ case VHOST_USER_GPU_DMABUF_SCANOUT2:
+ return sizeof(VhostUserGpuDMABUFScanout2);
+ case VHOST_USER_GPU_DMABUF_UPDATE:
+ case VHOST_USER_GPU_UPDATE:
+ return sizeof(VhostUserGpuUpdate);
+ default:
+ return 0;
+ }
+}
+
static void
vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
{
@@ -322,6 +347,14 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
break;
}
+
+ if ((uint64_t)m->width * m->height >
+ (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) {
+ error_report("vhost-user-gpu: update payload too small"
+ " for %ux%u", m->width, m->height);
+ break;
+ }
+
s = &g->parent_obj.scanout[m->scanout_id];
con = s->con;
pixman_image_t *image =
@@ -396,6 +429,11 @@ vhost_user_gpu_chr_read(void *opaque)
msg->flags = flags;
msg->size = size;
+ if (size < vhost_user_gpu_min_payload_size(request)) {
+ error_report("vhost-user-gpu: message %d payload too small", request);
+ goto end;
+ }
+
if (request == VHOST_USER_GPU_CURSOR_UPDATE ||
request == VHOST_USER_GPU_CURSOR_POS ||
request == VHOST_USER_GPU_CURSOR_POS_HIDE) {
--
2.55.0
On 13/07/2026 14.51, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> A malicious or buggy vhost-user-gpu backend can send messages with
> undersized payloads, leading to out-of-bounds reads when the handler
> accesses struct fields beyond the allocated buffer. However,
> vhost-user-gpu is considered trusted by QEMU by design (it has access to
> shared memory etc).
>
> Add a centralized minimum payload size check in vhost_user_gpu_chr_read()
> that rejects messages before dispatch, and a per-pixel bounds check in
> the VHOST_USER_GPU_UPDATE handler to ensure the variable-length data
> covers the declared width x height.
>
> Fixes: 267f66465 ("hw/display: add vhost-user-vga & gpu-pci")
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3866
> Reported-by: Feifan Qian <bea1e@proton.me>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> hw/display/vhost-user-gpu.c | 38 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
Reviewed-by: Thomas Huth <thuth@redhat.com>
© 2016 - 2026 Red Hat, Inc.