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

virtio_gpu_scanout_blob_to_fb() computes the framebuffer offset from
guest-controlled offsets[0], r.x, r.y and stride using uint32_t
arithmetic. The sum can wrap around, so a small fb->offset passes the
fbend > blob_size check even though the real offset lies outside the
blob. The wrapped value is later used as data + fb->offset in
virtio_gpu_do_set_scanout(), letting the guest point the scanout at
host memory beyond the blob.

Compute the offset in uint64_t, reject values wider than UINT32_MAX
(the width of fb->offset), and only store into fb->offset once both
range checks pass.

Fixes: CVE-2026-66019
Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3871
Reported-by: Cyber_black <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
---
 hw/display/virtio-gpu.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 36d2bd9edee6..d1e32840e223 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -784,7 +784,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct 
virtio_gpu_framebuffer *fb,
                                    struct virtio_gpu_set_scanout_blob *ss,
                                    uint64_t blob_size)
 {
-    uint64_t fbend;
+    uint64_t fbend, offset;
     uint32_t bytes_pp;
 
     fb->format = virtio_gpu_get_pixman_format(ss->format);
@@ -814,18 +814,20 @@ bool virtio_gpu_scanout_blob_to_fb(struct 
virtio_gpu_framebuffer *fb,
         return false;
     }
 
-    fb->offset = ss->offsets[0] + ss->r.x * bytes_pp + ss->r.y * fb->stride;
+    offset = (uint64_t)ss->offsets[0] + (uint64_t)ss->r.x * bytes_pp +
+             (uint64_t)ss->r.y * fb->stride;
 
-    fbend = fb->offset;
-    fbend += (uint64_t) fb->stride * ss->r.height;
+    fbend = offset + (uint64_t)fb->stride * ss->r.height;
 
-    if (fbend > blob_size) {
+    if (offset > UINT32_MAX || fbend > blob_size) {
         qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: fb end out of range\n",
+                      "%s: invalid fb bounds\n",
                       __func__);
         return false;
     }
 
+    fb->offset = offset;
+
     return true;
 }
 
-- 
2.55.0


Reply via email to