@samitouri / QOSamiQemu / commits / d513c644b8

hw/display/virtio-gpu: Always reject invalid scanout bounds

virtio-gpu does not consistently check scanout bounds with wraparound handling. In the unchecked virgl SET_SCANOUT path, guest dimensions reach qemu_console_resize(), qemu_create_displaysurface(), and ultimately qemu_pixman_image_new_shareable(..., &error_abort), so an invalid rectangle can terminate QEMU. Implement a check with proper wraparound handling and apply it consistently. Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Fixes: 7c092f17ccee ("virtio-gpu: Handle resource blob commands") Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260803-scanout-v1-1-c9831dafdab2@rsg.ci.i.u-tokyo.ac.jp>

Akihiko Odaki committed Aug 3, 2026 at 17:45 UTC d513c644b89361ab645a50a627a1d846aefc3eb2
4 files changed +42 -25
hw/display/virtio-gpu-rutabaga.c
+6
@@ -315,6 +315,12 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd)
315 res = virtio_gpu_find_resource(g, ss.resource_id);
316 CHECK(res, cmd);
317
318 + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id,
319 + res->width, res->height, &ss.r,
320 + &cmd->error)) {
321 + return;
322 + }
323 +
324 if (!res->image) {
325 pixman_format_code_t pformat;
326 pformat = virtio_gpu_get_pixman_format(res->format);
hw/display/virtio-gpu-virgl.c
+9 -11
@@ -560,7 +560,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
560 }
561 g->parent_obj.enable = 1;
562
563 - if (ss.resource_id && ss.r.width && ss.r.height) {
563 + if (ss.resource_id) {
564 struct virgl_renderer_resource_info info;
565 void *d3d_tex2d = NULL;
566
@@ -581,6 +581,11 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
581 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
582 return;
583 }
584 + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id,
585 + info.width, info.height, &ss.r,
586 + &cmd->error)) {
587 + return;
588 + }
589 qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con,
590 ss.r.width, ss.r.height);
591 virgl_renderer_force_ctx_0();
@@ -987,16 +992,9 @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g,
992 return;
993 }
994
990 - if (ss.width < 16 ||
991 - ss.height < 16 ||
992 - ss.r.x + ss.r.width > ss.width ||
993 - ss.r.y + ss.r.height > ss.height) {
994 - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
995 - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n",
996 - __func__, ss.scanout_id, ss.resource_id,
997 - ss.r.x, ss.r.y, ss.r.width, ss.r.height,
998 - ss.width, ss.height);
999 - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
995 + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id,
996 + ss.width, ss.height, &ss.r,
997 + &cmd->error)) {
998 return;
999 }
1000
hw/display/virtio-gpu.c
+22 -14
@@ -633,6 +633,26 @@ static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format)
633 return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
634 }
635
636 +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id,
637 + uint32_t width, uint32_t height,
638 + const struct virtio_gpu_rect *r,
639 + uint32_t *error)
640 +{
641 + if (r->width < 16 ||
642 + r->height < 16 ||
643 + (uint64_t)r->x + r->width > width ||
644 + (uint64_t)r->y + r->height > height) {
645 + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
646 + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n",
647 + __func__, scanout_id, resource_id, width, height,
648 + r->x, r->y, r->width, r->height);
649 + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
650 + return false;
651 + }
652 +
653 + return true;
654 +}
655 +
656 static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
657 uint32_t scanout_id,
658 struct virtio_gpu_framebuffer *fb,
@@ -646,20 +666,8 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
666
667 scanout = &g->parent_obj.scanout[scanout_id];
668
649 - if (r->x > fb->width ||
650 - r->y > fb->height ||
651 - r->width < 16 ||
652 - r->height < 16 ||
653 - r->width > fb->width ||
654 - r->height > fb->height ||
655 - r->x + r->width > fb->width ||
656 - r->y + r->height > fb->height) {
657 - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
658 - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n",
659 - __func__, scanout_id, res->resource_id,
660 - r->x, r->y, r->width, r->height,
661 - fb->width, fb->height);
662 - *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
669 + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id,
670 + fb->width, fb->height, r, error)) {
671 return false;
672 }
673
include/hw/virtio/virtio-gpu.h
+5
@@ -366,6 +366,11 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
366 struct virtio_gpu_scanout *s,
367 uint32_t resource_id);
368
369 +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id,
370 + uint32_t width, uint32_t height,
371 + const struct virtio_gpu_rect *r,
372 + uint32_t *error);
373 +
374 /**
375 * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data
376 * fb: the frame-buffer descriptor to fill out