@samitouri / QOSamiQemu / commits / 95687639e6

hw/display/vga: fix panning_buf OOB after text/graphics switch

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>

Marc-André Lureau committed Jul 28, 2026 at 19:14 UTC 95687639e647ec917226e6d3a6713a2b373e1ffe
1 file changed +4 -3
hw/display/vga.c
+4 -3
@@ -1647,11 +1647,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
1647 s->last_line_offset = s->params.line_offset;
1648 s->last_depth = depth;
1649 s->last_byteswap = byteswap;
1650 - /* 16 extra pixels are needed for double-width planar modes. */
1651 - s->panning_buf = g_realloc(s->panning_buf,
1652 - (disp_width + 16) * sizeof(uint32_t));
1650 full_update = 1;
1651 }
1652 +
1653 + /* 16 extra pixels are needed for double-width planar modes. */
1654 + s->panning_buf = g_realloc(s->panning_buf,
1655 + (disp_width + 16) * sizeof(uint32_t));
1656 if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4)
1657 && !surface_is_allocated(surface)) {
1658 /* base address changed (page flip) -> shared display surfaces