:p
atchew
Login
The following changes since commit b428fe036233cbd15d37e3c027ab6ca4d3661a80: Merge tag 'pull-target-arm-20260731' of https://gitlab.com/pm215/qemu into staging (2026-07-31 16:19:04 -0400) are available in the Git repository at: https://gitlab.com/marcandre.lureau/qemu.git tags/fix-pr-v1 for you to fetch changes up to 8876b366d7cac1f65978b8f8472725b980a1abfb: qapi/dump: add allowed-by-guest feature to win-dmp (2026-08-04 11:18:43 +0400) ---------------------------------------------------------------- Fixes for 11.1-rc Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> ---------------------------------------------------------------- Akihiko Odaki (1): hw/display/virtio-gpu: Always reject invalid scanout bounds Ankur Saini (1): virtio-gpu: reject requests with short/truncated control headers Bin Guo (1): hw/display/virtio-gpu: Unmap DMA regions on reset Denis V. Lunev (1): qapi/dump: add allowed-by-guest feature to win-dmp Marc-André Lureau (5): hw/display/virtio-gpu: validate blob iov size hw/display/vga: fix panning_buf OOB after text/graphics switch vhost-user-gpu: fix integer overflow in buffer allocation hw/display/virtio-gpu: fix offset wraparound in scanout_blob_to_fb hw/display/virtio-gpu: drop redundant node->value NULL checks qapi/dump.json | 9 +++- contrib/vhost-user-gpu/vugpu.h | 1 + include/hw/virtio/virtio-gpu.h | 8 ++++ contrib/vhost-user-gpu/vhost-user-gpu.c | 29 +++++++----- contrib/vhost-user-gpu/vugbm.c | 11 ++++- hw/display/vga.c | 7 +-- hw/display/virtio-gpu-base.c | 4 +- hw/display/virtio-gpu-rutabaga.c | 6 +++ hw/display/virtio-gpu-virgl.c | 20 ++++----- hw/display/virtio-gpu.c | 80 ++++++++++++++++++++++++--------- 10 files changed, 126 insertions(+), 49 deletions(-)
virtio_gpu_resource_create_blob() stores the guest-controlled blob_size without checking it against the total size of the iov backing entries. Since both values are independently guest-controlled, a malicious guest can set blob_size much larger than the actual iov backing. Subsequent SET_SCANOUT_BLOB checks bounds against the inflated blob_size, allowing a pixman surface to be created over the undersized buffer. Any display refresh then reads past the actual allocation, potentially crashing QEMU or leaking host memory contents depending on the backing type. Validate that the iov backing is at least as large as the declared blob_size in create_blob (when nr_entries > 0, since the spec permits deferred backing), attach_backing (when attaching to a blob resource), and the blob migration load path. Fixes: CVE-2026-66021 Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3945 Reported-by: "sundayjiang(蒋浩天)" <sundayjiang@tencent.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260729161431.1180691-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, return; } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -XXX,XX +XXX,XX @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + return; + } + if (!res->image) { virtio_gpu_init_udmabuf(res); } @@ -XXX,XX +XXX,XX @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->iov[i].iov_len = qemu_get_be32(f); } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + if (!virtio_gpu_load_restore_mapping(g, res)) { g_free(res); return -EINVAL; -- 2.55.0
The fields last_width and last_height serve two purposes: the text renderer counts in characters, the graphics renderer in pixels. panning_buf reallocation is guarded by geometry-change check, so the unit mismatch can trick it into thinking nothing changed when the resolution actually grew. A guest can trigger this by switching graphics -> text -> graphics: 1. Enter graphics mode with a small width (CR01=0x00, 8 pixels). The predicate fires and panning_buf is allocated for that width. 2. Switch to text mode with a large width (CR01=0xFF, 256 chars). The text renderer stores 256 into last_width. The text path never touches panning_buf. 3. Switch back to graphics with a width that happens to equal 256 in pixels (CR01=0x1F, 32*8 = 256). The predicate sees 256 == 256 and skips the realloc. With horizontal pel panning enabled, vga_draw_line4() then writes a full 256-pixel scanline into the buffer still sized for 8 pixels -- a 960-byte heap overflow on every scanline, every refresh. Fix it by reallocating unconditionally panning_buf on vga_draw_graphic(). Fixes: CVE-2026-17516 Fixes: 973a724eb006 ("vga: implement horizontal pel panning in graphics modes") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4085 Cc: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Warisjeet Singh <sinxx198@gmail.com> [ Marc- André - drop realloc() resize condition & commit message ] Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260728151456.3704099-1-marcandre.lureau@redhat.com> --- hw/display/vga.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -XXX,XX +XXX,XX @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->last_line_offset = s->params.line_offset; s->last_depth = depth; s->last_byteswap = byteswap; - /* 16 extra pixels are needed for double-width planar modes. */ - s->panning_buf = g_realloc(s->panning_buf, - (disp_width + 16) * sizeof(uint32_t)); full_update = 1; } + + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces -- 2.55.0
A malicious guest can trigger a heap buffer overflow in the vhost-user-gpu backend by sending a VIRTIO_GPU_CMD_RESOURCE_CREATE_2D with large width and height values (e.g. 65537x65537). The allocation size width * height * 4 silently wraps in uint32_t arithmetic, resulting in a much smaller allocation than expected. Subsequent VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D writes past the heap buffer. The in-tree virtio-gpu device (hw/display/virtio-gpu.c) already handles this via calc_image_hostmem() with uint64_t arithmetic and an overflow check. Apply the same approach to the vhost-user-gpu contrib backend: - Add an overflow check in vugbm_buffer_create() rejecting dimensions where width * height * 4 exceeds UINT32_MAX - Promote the size arithmetic to uint64_t in mem_alloc_bo() and udmabuf_get_size() - Check the return value of vugbm_buffer_create() in vg_resource_create_2d(), which was previously ignored Fixes: CVE-2026-15264 Reported-by: "Vulnerability Report" <vr@darknavy.com> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3940 Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com> Acked-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260710134720.2317856-1-marcandre.lureau@redhat.com> --- contrib/vhost-user-gpu/vhost-user-gpu.c | 8 +++++++- contrib/vhost-user-gpu/vugbm.c | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -XXX,XX +XXX,XX @@ vg_resource_create_2d(VuGpu *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } - vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + if (!vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height)) { + g_critical("%s: buffer creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -XXX,XX +XXX,XX @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc(buf->width * buf->height * 4); + buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4); buf->stride = buf->width * 4; return true; } @@ -XXX,XX +XXX,XX @@ struct udmabuf_create { static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); + return ROUND_UP((uint64_t)buf->width * buf->height * 4, + qemu_real_host_page_size()); } static bool @@ -XXX,XX +XXX,XX @@ bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { + uint64_t size = (uint64_t)width * height * 4; + if (size > UINT32_MAX) { + g_warning("buffer dimensions too large: %ux%u", width, height); + return false; + } + buffer->dev = dev; buffer->width = width; buffer->height = height; -- 2.55.0
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. When the sum exceeds UINT32_MAX, silent wraparound lets the guest steer the scanout to an arbitrary in-bounds region of the blob instead of the intended rectangle. Compute the offset in uint64_t, reject values exceeding UINT32_MAX (the width of fb->offset), and only store into fb->offset once both range checks pass. ("[PATCH] hw/display/virtio-gpu: Remove the bytes_pp field") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3871 Based-on: <20260719-bpp-v1-1-9b91946d6cf3@rsg.ci.i.u-tokyo.ac.jp> Reported-by: Cyber_black <Cyberblackk@proton.me> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260725122734.1775774-1-marcandre.lureau@redhat.com> --- 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 XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ 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); @@ -XXX,XX +XXX,XX @@ 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
QAPI-generated list visitors guarantee that node->value is never NULL: the input visitor allocates it via g_malloc0() in visit_start_struct(), and on failure the entire list parse is aborted and freed. Remove the unnecessary NULL checks from both callsites iterating g->conf.outputs. Resolves: Coverity CID 1664272 Fixes: 8dc8449a678f ("hw/display/virtio-gpu: Avoid leaking migration blocker") Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260730114751.3515083-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu-base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, for (output_idx = 0, node = g->conf.outputs; output_idx <= scanout && node; output_idx++, node = node->next) { - if (output_idx == scanout && node->value && node->value->name) { + if (output_idx == scanout && node->value->name) { info.name = node->value->name; break; } @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_device_realize(DeviceState *qdev, error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); return false; } - if (node->value && node->value->name && + if (node->value->name && strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { error_setg(errp, "invalid output name '%s' > %d", node->value->name, EDID_NAME_MAX_LENGTH); -- 2.55.0
From: Ankur Saini <ankur98saini@gmail.com> A short control request can leave command data partially initialized. For the common header, guest-controlled flags can then cause stale fence metadata to be returned to the guest. The command fill helpers detect a short copy but only log and return. For the common header this leaves the request without any completion; for type-specific commands the caller still completes the request but reports VIRTIO_GPU_RESP_OK_NODATA, masking the error. Make VIRTIO_GPU_FILL_CMD() clear the partially copied object and complete the request with ERR_INVALID_PARAMETER. Make VUGPU_FILL_CMD() report the same error through the existing vhost-user-gpu dispatcher. This also rejects truncated type-specific commands. The vhost-user-gpu common header is copied outside VUGPU_FILL_CMD(), so clear it and complete the request directly when that copy is short. Fixes: CVE-2026-66021 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4094 Reported-by: Ankur Saini <ankur98saini@gmail.com> Suggested-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Ankur Saini <ankur98saini@gmail.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260803-virtio-gpu-short-header-v3-1-936c1daa8e61@gmail.com> --- contrib/vhost-user-gpu/vugpu.h | 1 + include/hw/virtio/virtio-gpu.h | 3 +++ contrib/vhost-user-gpu/vhost-user-gpu.c | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -XXX,XX +XXX,XX @@ struct virtio_gpu_ctrl_command { if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ __func__, vugpufillcmd_s_, sizeof(out)); \ + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; \ return; \ } \ } while (0) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -XXX,XX +XXX,XX @@ struct VirtIOGPURutabaga { qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ __func__, virtiogpufillcmd_s_, sizeof(out)); \ + memset(&out, 0, sizeof(out)); \ + virtio_gpu_ctrl_response_nodata( \ + g, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); \ return; \ } \ } while (0) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -XXX,XX +XXX,XX @@ vg_handle_ctrl(VuDev *dev, int qidx) if (len != sizeof(cmd->cmd_hdr)) { g_warning("%s: command size incorrect %zu vs %zu\n", __func__, len, sizeof(cmd->cmd_hdr)); - } - - virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); - g_debug("%d %s\n", cmd->cmd_hdr.type, - vg_cmd_to_string(cmd->cmd_hdr.type)); - - if (vg->virgl) { - vg_virgl_process_cmd(vg, cmd); + memset(&cmd->cmd_hdr, 0, sizeof(cmd->cmd_hdr)); + vg_ctrl_response_nodata( + vg, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); } else { - vg_process_cmd(vg, cmd); + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } } if (cmd->state != VG_CMD_STATE_FINISHED) { -- 2.55.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> 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> --- include/hw/virtio/virtio-gpu.h | 5 +++++ hw/display/virtio-gpu-rutabaga.c | 6 ++++++ hw/display/virtio-gpu-virgl.c | 20 +++++++++----------- hw/display/virtio-gpu.c | 36 ++++++++++++++++++++++-------------- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -XXX,XX +XXX,XX @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error); + /** * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data * fb: the frame-buffer descriptor to fill out diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -XXX,XX +XXX,XX @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) res = virtio_gpu_find_resource(g, ss.resource_id); CHECK(res, cmd); + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + res->width, res->height, &ss.r, + &cmd->error)) { + return; + } + if (!res->image) { pixman_format_code_t pformat; pformat = virtio_gpu_get_pixman_format(res->format); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, } g->parent_obj.enable = 1; - if (ss.resource_id && ss.r.width && ss.r.height) { + if (ss.resource_id) { struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + info.width, info.height, &ss.r, + &cmd->error)) { + return; + } qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, return; } - if (ss.width < 16 || - ss.height < 16 || - ss.r.x + ss.r.width > ss.width || - ss.r.y + ss.r.height > ss.height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, ss.scanout_id, ss.resource_id, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, - ss.width, ss.height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + ss.width, ss.height, &ss.r, + &cmd->error)) { return; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); } +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error) +{ + if (r->width < 16 || + r->height < 16 || + (uint64_t)r->x + r->width > width || + (uint64_t)r->y + r->height > height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n", + __func__, scanout_id, resource_id, width, height, + r->x, r->y, r->width, r->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + return true; +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -XXX,XX +XXX,XX @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, scanout = &g->parent_obj.scanout[scanout_id]; - if (r->x > fb->width || - r->y > fb->height || - r->width < 16 || - r->height < 16 || - r->width > fb->width || - r->height > fb->height || - r->x + r->width > fb->width || - r->y + r->height > fb->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, scanout_id, res->resource_id, - r->x, r->y, r->width, r->height, - fb->width, fb->height); - *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id, + fb->width, fb->height, r, error)) { return false; } -- 2.55.0
From: Bin Guo <guobin@linux.alibaba.com> virtio_gpu_reset() freed in-flight commands without unmapping the DMA regions acquired by virtqueue_pop(). Call virtqueue_detach_element() before g_free() in both drain loops. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3467 Cc: qemu-stable@nongnu.org Signed-off-by: Bin Guo <guobin@linux.alibaba.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260803082158.62998-1-guobin@linux.alibaba.com> --- hw/display/virtio-gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ void virtio_gpu_reset(VirtIODevice *vdev) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); QTAILQ_REMOVE(&g->cmdq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g_free(cmd); } while (!QTAILQ_EMPTY(&g->fenceq)) { cmd = QTAILQ_FIRST(&g->fenceq); QTAILQ_REMOVE(&g->fenceq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g->inflight--; g_free(cmd); } -- 2.55.0
From: "Denis V. Lunev" <den@openvz.org> Commit 1c0e259c5a35 ("dump: make win_dump_available() check vmcoreinfo for a Windows dump header") changed two things in a way that is visible to QMP clients but not to introspection: query-dump-guest-memory-capability now lists win-dmp only for a guest that has published a Windows dump header through the vmcoreinfo device, and dump-guest-memory, which shares win_dump_available(), rejects the format otherwise. Before that, both accepted win-dmp on any x86 machine. A client that wants to select win-dmp automatically therefore cannot trust the capability query on its own: on an older QEMU it reports win-dmp for every x86 guest, Linux ones included, where the resulting dump is useless. libvirt ran into exactly this while picking a format for on_crash and watchdog triggered dumps, and has no way to tell the two behaviours apart. Add an 'allowed-by-guest' feature to the win-dmp member of DumpGuestMemoryFormat so the fixed behaviour becomes discoverable. DumpGuestMemoryFormat is reachable from both query-dump-guest-memory-capability's return type and dump-guest-memory's arguments, so a single flag covers both halves of the change. Where the feature is absent, a reported win-dmp says nothing about the guest, and a client that needs the dump to be loadable afterwards should fall back to elf. CC: Eric Blake <eblake@redhat.com> CC: Markus Armbruster <armbru@redhat.com> CC: "Marc-André Lureau" <marcandre.lureau@redhat.com> Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Denis V. Lunev <den@openvz.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260731155001.1204103-1-den@openvz.org> --- qapi/dump.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qapi/dump.json b/qapi/dump.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -XXX,XX +XXX,XX @@ # @win-dmp: Windows full crashdump format, can be used instead of ELF # converting (since 2.13) # +# Features: +# +# @allowed-by-guest: If present, @win-dmp is listed by +# `query-dump-guest-memory-capability`, and accepted by +# `dump-guest-memory`, only when the guest has published a Windows +# dump header through the vmcoreinfo device (since 11.1) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', @@ -XXX,XX +XXX,XX @@ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', - 'win-dmp' ] } + { 'name': 'win-dmp', 'features': ['allowed-by-guest'] } ] } ## # @dump-guest-memory: -- 2.55.0
The following changes since commit b428fe036233cbd15d37e3c027ab6ca4d3661a80: Merge tag 'pull-target-arm-20260731' of https://gitlab.com/pm215/qemu into staging (2026-07-31 16:19:04 -0400) are available in the Git repository at: https://gitlab.com/marcandre.lureau/qemu.git tags/fix-pr-v2 for you to fetch changes up to dd272c8c87f165d41c945946242c5c8619e7122f: qapi/dump: add allowed-by-guest feature to win-dmp (2026-08-04 23:21:40 +0400) ---------------------------------------------------------------- Fixes for 11.1-rc Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- v2: - fix CVE# in "virtio-gpu: reject requests with short/truncated control headers" ---------------------------------------------------------------- Akihiko Odaki (1): hw/display/virtio-gpu: Always reject invalid scanout bounds Ankur Saini (1): virtio-gpu: reject requests with short/truncated control headers Bin Guo (1): hw/display/virtio-gpu: Unmap DMA regions on reset Denis V. Lunev (1): qapi/dump: add allowed-by-guest feature to win-dmp Marc-André Lureau (5): hw/display/virtio-gpu: validate blob iov size hw/display/vga: fix panning_buf OOB after text/graphics switch vhost-user-gpu: fix integer overflow in buffer allocation hw/display/virtio-gpu: fix offset wraparound in scanout_blob_to_fb hw/display/virtio-gpu: drop redundant node->value NULL checks qapi/dump.json | 9 +++- contrib/vhost-user-gpu/vugpu.h | 1 + include/hw/virtio/virtio-gpu.h | 8 ++++ contrib/vhost-user-gpu/vhost-user-gpu.c | 29 +++++++----- contrib/vhost-user-gpu/vugbm.c | 11 ++++- hw/display/vga.c | 7 +-- hw/display/virtio-gpu-base.c | 4 +- hw/display/virtio-gpu-rutabaga.c | 6 +++ hw/display/virtio-gpu-virgl.c | 20 ++++----- hw/display/virtio-gpu.c | 80 ++++++++++++++++++++++++--------- 10 files changed, 126 insertions(+), 49 deletions(-)
virtio_gpu_resource_create_blob() stores the guest-controlled blob_size without checking it against the total size of the iov backing entries. Since both values are independently guest-controlled, a malicious guest can set blob_size much larger than the actual iov backing. Subsequent SET_SCANOUT_BLOB checks bounds against the inflated blob_size, allowing a pixman surface to be created over the undersized buffer. Any display refresh then reads past the actual allocation, potentially crashing QEMU or leaking host memory contents depending on the backing type. Validate that the iov backing is at least as large as the declared blob_size in create_blob (when nr_entries > 0, since the spec permits deferred backing), attach_backing (when attaching to a blob resource), and the blob migration load path. Fixes: CVE-2026-66021 Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3945 Reported-by: "sundayjiang(蒋浩天)" <sundayjiang@tencent.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260729161431.1180691-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, return; } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -XXX,XX +XXX,XX @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + return; + } + if (!res->image) { virtio_gpu_init_udmabuf(res); } @@ -XXX,XX +XXX,XX @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->iov[i].iov_len = qemu_get_be32(f); } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + if (!virtio_gpu_load_restore_mapping(g, res)) { g_free(res); return -EINVAL; -- 2.55.0
The fields last_width and last_height serve two purposes: the text renderer counts in characters, the graphics renderer in pixels. panning_buf reallocation is guarded by geometry-change check, so the unit mismatch can trick it into thinking nothing changed when the resolution actually grew. A guest can trigger this by switching graphics -> text -> graphics: 1. Enter graphics mode with a small width (CR01=0x00, 8 pixels). The predicate fires and panning_buf is allocated for that width. 2. Switch to text mode with a large width (CR01=0xFF, 256 chars). The text renderer stores 256 into last_width. The text path never touches panning_buf. 3. Switch back to graphics with a width that happens to equal 256 in pixels (CR01=0x1F, 32*8 = 256). The predicate sees 256 == 256 and skips the realloc. With horizontal pel panning enabled, vga_draw_line4() then writes a full 256-pixel scanline into the buffer still sized for 8 pixels -- a 960-byte heap overflow on every scanline, every refresh. Fix it by reallocating unconditionally panning_buf on vga_draw_graphic(). Fixes: CVE-2026-17516 Fixes: 973a724eb006 ("vga: implement horizontal pel panning in graphics modes") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4085 Cc: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Warisjeet Singh <sinxx198@gmail.com> [ Marc- André - drop realloc() resize condition & commit message ] Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260728151456.3704099-1-marcandre.lureau@redhat.com> --- hw/display/vga.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -XXX,XX +XXX,XX @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->last_line_offset = s->params.line_offset; s->last_depth = depth; s->last_byteswap = byteswap; - /* 16 extra pixels are needed for double-width planar modes. */ - s->panning_buf = g_realloc(s->panning_buf, - (disp_width + 16) * sizeof(uint32_t)); full_update = 1; } + + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces -- 2.55.0
A malicious guest can trigger a heap buffer overflow in the vhost-user-gpu backend by sending a VIRTIO_GPU_CMD_RESOURCE_CREATE_2D with large width and height values (e.g. 65537x65537). The allocation size width * height * 4 silently wraps in uint32_t arithmetic, resulting in a much smaller allocation than expected. Subsequent VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D writes past the heap buffer. The in-tree virtio-gpu device (hw/display/virtio-gpu.c) already handles this via calc_image_hostmem() with uint64_t arithmetic and an overflow check. Apply the same approach to the vhost-user-gpu contrib backend: - Add an overflow check in vugbm_buffer_create() rejecting dimensions where width * height * 4 exceeds UINT32_MAX - Promote the size arithmetic to uint64_t in mem_alloc_bo() and udmabuf_get_size() - Check the return value of vugbm_buffer_create() in vg_resource_create_2d(), which was previously ignored Fixes: CVE-2026-15264 Reported-by: "Vulnerability Report" <vr@darknavy.com> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3940 Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com> Acked-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260710134720.2317856-1-marcandre.lureau@redhat.com> --- contrib/vhost-user-gpu/vhost-user-gpu.c | 8 +++++++- contrib/vhost-user-gpu/vugbm.c | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -XXX,XX +XXX,XX @@ vg_resource_create_2d(VuGpu *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } - vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + if (!vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height)) { + g_critical("%s: buffer creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -XXX,XX +XXX,XX @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc(buf->width * buf->height * 4); + buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4); buf->stride = buf->width * 4; return true; } @@ -XXX,XX +XXX,XX @@ struct udmabuf_create { static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); + return ROUND_UP((uint64_t)buf->width * buf->height * 4, + qemu_real_host_page_size()); } static bool @@ -XXX,XX +XXX,XX @@ bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { + uint64_t size = (uint64_t)width * height * 4; + if (size > UINT32_MAX) { + g_warning("buffer dimensions too large: %ux%u", width, height); + return false; + } + buffer->dev = dev; buffer->width = width; buffer->height = height; -- 2.55.0
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. When the sum exceeds UINT32_MAX, silent wraparound lets the guest steer the scanout to an arbitrary in-bounds region of the blob instead of the intended rectangle. Compute the offset in uint64_t, reject values exceeding UINT32_MAX (the width of fb->offset), and only store into fb->offset once both range checks pass. ("[PATCH] hw/display/virtio-gpu: Remove the bytes_pp field") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3871 Based-on: <20260719-bpp-v1-1-9b91946d6cf3@rsg.ci.i.u-tokyo.ac.jp> Reported-by: Cyber_black <Cyberblackk@proton.me> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260725122734.1775774-1-marcandre.lureau@redhat.com> --- 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 XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ 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); @@ -XXX,XX +XXX,XX @@ 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
QAPI-generated list visitors guarantee that node->value is never NULL: the input visitor allocates it via g_malloc0() in visit_start_struct(), and on failure the entire list parse is aborted and freed. Remove the unnecessary NULL checks from both callsites iterating g->conf.outputs. Resolves: Coverity CID 1664272 Fixes: 8dc8449a678f ("hw/display/virtio-gpu: Avoid leaking migration blocker") Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260730114751.3515083-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu-base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, for (output_idx = 0, node = g->conf.outputs; output_idx <= scanout && node; output_idx++, node = node->next) { - if (output_idx == scanout && node->value && node->value->name) { + if (output_idx == scanout && node->value->name) { info.name = node->value->name; break; } @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_device_realize(DeviceState *qdev, error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); return false; } - if (node->value && node->value->name && + if (node->value->name && strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { error_setg(errp, "invalid output name '%s' > %d", node->value->name, EDID_NAME_MAX_LENGTH); -- 2.55.0
From: Ankur Saini <ankur98saini@gmail.com> A short control request can leave command data partially initialized. For the common header, guest-controlled flags can then cause stale fence metadata to be returned to the guest. The command fill helpers detect a short copy but only log and return. For the common header this leaves the request without any completion; for type-specific commands the caller still completes the request but reports VIRTIO_GPU_RESP_OK_NODATA, masking the error. Make VIRTIO_GPU_FILL_CMD() clear the partially copied object and complete the request with ERR_INVALID_PARAMETER. Make VUGPU_FILL_CMD() report the same error through the existing vhost-user-gpu dispatcher. This also rejects truncated type-specific commands. The vhost-user-gpu common header is copied outside VUGPU_FILL_CMD(), so clear it and complete the request directly when that copy is short. Fixes: CVE-2026-18054 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4094 Reported-by: Ankur Saini <ankur98saini@gmail.com> Suggested-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Ankur Saini <ankur98saini@gmail.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260803-virtio-gpu-short-header-v3-1-936c1daa8e61@gmail.com> --- contrib/vhost-user-gpu/vugpu.h | 1 + include/hw/virtio/virtio-gpu.h | 3 +++ contrib/vhost-user-gpu/vhost-user-gpu.c | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -XXX,XX +XXX,XX @@ struct virtio_gpu_ctrl_command { if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ __func__, vugpufillcmd_s_, sizeof(out)); \ + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; \ return; \ } \ } while (0) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -XXX,XX +XXX,XX @@ struct VirtIOGPURutabaga { qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ __func__, virtiogpufillcmd_s_, sizeof(out)); \ + memset(&out, 0, sizeof(out)); \ + virtio_gpu_ctrl_response_nodata( \ + g, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); \ return; \ } \ } while (0) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -XXX,XX +XXX,XX @@ vg_handle_ctrl(VuDev *dev, int qidx) if (len != sizeof(cmd->cmd_hdr)) { g_warning("%s: command size incorrect %zu vs %zu\n", __func__, len, sizeof(cmd->cmd_hdr)); - } - - virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); - g_debug("%d %s\n", cmd->cmd_hdr.type, - vg_cmd_to_string(cmd->cmd_hdr.type)); - - if (vg->virgl) { - vg_virgl_process_cmd(vg, cmd); + memset(&cmd->cmd_hdr, 0, sizeof(cmd->cmd_hdr)); + vg_ctrl_response_nodata( + vg, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); } else { - vg_process_cmd(vg, cmd); + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } } if (cmd->state != VG_CMD_STATE_FINISHED) { -- 2.55.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> 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> --- include/hw/virtio/virtio-gpu.h | 5 +++++ hw/display/virtio-gpu-rutabaga.c | 6 ++++++ hw/display/virtio-gpu-virgl.c | 20 +++++++++----------- hw/display/virtio-gpu.c | 36 ++++++++++++++++++++++-------------- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -XXX,XX +XXX,XX @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error); + /** * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data * fb: the frame-buffer descriptor to fill out diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -XXX,XX +XXX,XX @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) res = virtio_gpu_find_resource(g, ss.resource_id); CHECK(res, cmd); + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + res->width, res->height, &ss.r, + &cmd->error)) { + return; + } + if (!res->image) { pixman_format_code_t pformat; pformat = virtio_gpu_get_pixman_format(res->format); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, } g->parent_obj.enable = 1; - if (ss.resource_id && ss.r.width && ss.r.height) { + if (ss.resource_id) { struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + info.width, info.height, &ss.r, + &cmd->error)) { + return; + } qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); @@ -XXX,XX +XXX,XX @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, return; } - if (ss.width < 16 || - ss.height < 16 || - ss.r.x + ss.r.width > ss.width || - ss.r.y + ss.r.height > ss.height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, ss.scanout_id, ss.resource_id, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, - ss.width, ss.height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + ss.width, ss.height, &ss.r, + &cmd->error)) { return; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); } +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error) +{ + if (r->width < 16 || + r->height < 16 || + (uint64_t)r->x + r->width > width || + (uint64_t)r->y + r->height > height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n", + __func__, scanout_id, resource_id, width, height, + r->x, r->y, r->width, r->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + return true; +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -XXX,XX +XXX,XX @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, scanout = &g->parent_obj.scanout[scanout_id]; - if (r->x > fb->width || - r->y > fb->height || - r->width < 16 || - r->height < 16 || - r->width > fb->width || - r->height > fb->height || - r->x + r->width > fb->width || - r->y + r->height > fb->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, scanout_id, res->resource_id, - r->x, r->y, r->width, r->height, - fb->width, fb->height); - *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id, + fb->width, fb->height, r, error)) { return false; } -- 2.55.0
From: Bin Guo <guobin@linux.alibaba.com> virtio_gpu_reset() freed in-flight commands without unmapping the DMA regions acquired by virtqueue_pop(). Call virtqueue_detach_element() before g_free() in both drain loops. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3467 Cc: qemu-stable@nongnu.org Signed-off-by: Bin Guo <guobin@linux.alibaba.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260803082158.62998-1-guobin@linux.alibaba.com> --- hw/display/virtio-gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -XXX,XX +XXX,XX @@ void virtio_gpu_reset(VirtIODevice *vdev) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); QTAILQ_REMOVE(&g->cmdq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g_free(cmd); } while (!QTAILQ_EMPTY(&g->fenceq)) { cmd = QTAILQ_FIRST(&g->fenceq); QTAILQ_REMOVE(&g->fenceq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g->inflight--; g_free(cmd); } -- 2.55.0
From: "Denis V. Lunev" <den@openvz.org> Commit 1c0e259c5a35 ("dump: make win_dump_available() check vmcoreinfo for a Windows dump header") changed two things in a way that is visible to QMP clients but not to introspection: query-dump-guest-memory-capability now lists win-dmp only for a guest that has published a Windows dump header through the vmcoreinfo device, and dump-guest-memory, which shares win_dump_available(), rejects the format otherwise. Before that, both accepted win-dmp on any x86 machine. A client that wants to select win-dmp automatically therefore cannot trust the capability query on its own: on an older QEMU it reports win-dmp for every x86 guest, Linux ones included, where the resulting dump is useless. libvirt ran into exactly this while picking a format for on_crash and watchdog triggered dumps, and has no way to tell the two behaviours apart. Add an 'allowed-by-guest' feature to the win-dmp member of DumpGuestMemoryFormat so the fixed behaviour becomes discoverable. DumpGuestMemoryFormat is reachable from both query-dump-guest-memory-capability's return type and dump-guest-memory's arguments, so a single flag covers both halves of the change. Where the feature is absent, a reported win-dmp says nothing about the guest, and a client that needs the dump to be loadable afterwards should fall back to elf. CC: Eric Blake <eblake@redhat.com> CC: Markus Armbruster <armbru@redhat.com> CC: "Marc-André Lureau" <marcandre.lureau@redhat.com> Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Denis V. Lunev <den@openvz.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260731155001.1204103-1-den@openvz.org> --- qapi/dump.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qapi/dump.json b/qapi/dump.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -XXX,XX +XXX,XX @@ # @win-dmp: Windows full crashdump format, can be used instead of ELF # converting (since 2.13) # +# Features: +# +# @allowed-by-guest: If present, @win-dmp is listed by +# `query-dump-guest-memory-capability`, and accepted by +# `dump-guest-memory`, only when the guest has published a Windows +# dump header through the vmcoreinfo device (since 11.1) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', @@ -XXX,XX +XXX,XX @@ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', - 'win-dmp' ] } + { 'name': 'win-dmp', 'features': ['allowed-by-guest'] } ] } ## # @dump-guest-memory: -- 2.55.0