[GIT PULL 2/9] hw/display/vga: fix panning_buf OOB after text/graphics switch
Marc-André Lureau <[email protected]> Tue, 04 Aug 2026 11:18:51 +0400
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]>
Signed-off-by: Warisjeet Singh <[email protected]>
[ Marc- André - drop realloc() resize condition & commit message ]
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
Message-ID: <[email protected]>
---
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 abe3f8e07758..da0c331486eb 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1647,11 +1647,12 @@ 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