On 2026/07/16 5:10, [email protected] wrote:
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: 144bd171c837 ("virtio-gpu: Support blob scanout using dmabuf fd") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3989 Signed-off-by: Marc-André Lureau <[email protected]> --- hw/display/virtio-gpu.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 89a73793a42..1d4b8ee4192 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -647,6 +647,14 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, return false; }+ if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) {
I found that fb->bytes_pp cannot be trusted because it is restored independently from the migration stream. I think that field should be removed from virtio_gpu_framebuffer; its migration slot can be preserved with VMSTATE_UNUSED_TEST(). Bytes per pixel can be derived from
fb->format, which Pixman actually uses. Regards, Akihiko Odaki
+ 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) {@@ -754,6 +762,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;
