@samitouri / QOSamiQemu / commits / 861372428b

hw/display/virtio-gpu: validate stride against width on scanout

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 <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Jul 15, 2026 at 11:55 UTC 861372428b05f74a1cf9a8af22a863aa7b46c7ce
1 file changed +16
hw/display/virtio-gpu.c
+16
@@ -646,6 +646,14 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
646 return false;
647 }
648
649 + if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) {
650 + qemu_log_mask(LOG_GUEST_ERROR,
651 + "%s: stride %u too small for width %u at %u bpp\n",
652 + __func__, fb->stride, fb->width, fb->bytes_pp);
653 + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
654 + return false;
655 + }
656 +
657 g->parent_obj.enable = 1;
658
659 if (res->blob) {
@@ -753,6 +761,14 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb,
761 fb->width = ss->width;
762 fb->height = ss->height;
763 fb->stride = ss->strides[0];
764 +
765 + if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) {
766 + qemu_log_mask(LOG_GUEST_ERROR,
767 + "%s: stride %u too small for width %u at %u bpp\n",
768 + __func__, fb->stride, fb->width, fb->bytes_pp);
769 + return false;
770 + }
771 +
772 fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride;
773
774 fbend = fb->offset;