From: Marc-AndrĂ© Lureau <[email protected]>

Validate that the framebuffer stride is at least width * bytes_per_pixel
in both virtio_gpu_scanout_blob_to_fb() and virtio_gpu_do_set_scanout().

A guest can set a very small stride while using a large width. The total
size check (offset + stride * height <= blob_size) passes because
stride * height is small, but pixman reads width * bytes_per_pixel per
row, causing heap OOB reads. The leaked data is rendered to the host
display.

The check is added in virtio_gpu_do_set_scanout() to cover all paths:
blob scanout, non-blob scanout and migration post_load. The additional
early check in virtio_gpu_scanout_blob_to_fb() rejects invalid blob
configurations early.

Fixes: CVE-2026-63109
Fixes: 7b5574225429 ("hw/display: check frame buffer can hold blob")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3989
Reported-by: Tristan @TristanInSec
Reviewed-by: Akihiko Odaki <[email protected]>
Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
(cherry picked from commit 861372428b05f74a1cf9a8af22a863aa7b46c7ce)
Signed-off-by: Michael Tokarev <[email protected]>

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 81d65ec57ca..10f55bd2ae8 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -646,6 +646,14 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
         return false;
     }
 
+    if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: stride %u too small for width %u at %u bpp\n",
+                      __func__, fb->stride, fb->width, fb->bytes_pp);
+        *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return false;
+    }
+
     g->parent_obj.enable = 1;
 
     if (res->blob) {
@@ -757,6 +765,14 @@ bool virtio_gpu_scanout_blob_to_fb(struct 
virtio_gpu_framebuffer *fb,
     fb->width = ss->width;
     fb->height = ss->height;
     fb->stride = ss->strides[0];
+
+    if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: stride %u too small for width %u at %u bpp\n",
+                      __func__, fb->stride, fb->width, fb->bytes_pp);
+        return false;
+    }
+
     fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * 
fb->stride;
 
     fbend = fb->offset;
-- 
2.47.3


Reply via email to