From: "Marc-André Lureau" <marcandre.lureau@redhat.com>
To: qemu-devel@nongnu.org
Cc: richard.henderson@linaro.org, Gerd Hoffmann <kraxel@redhat.com>
Subject: [GIT PULL 12/19] hw/display/vga: fix text-mode OOB write after a graphics surface switch
Date: Tue, 25 Aug 2026 15:21:02 +0400 [thread overview]
Message-ID: <20260825-fixes-v1-12-c59e8a620836@redhat.com> (raw)
In-Reply-To: <20260825-fixes-v1-0-c59e8a620836@redhat.com>
From: Warisjeet Singh <sinxx198@gmail.com>
vga_draw_text() decides whether the console surface needs a resize from
its geometry cache, but none of the cache terms observe the graphics
renderer having replaced the console surface in between:
- last_width/last_height are shared with vga_draw_graphic(), which
stores them in pixels while the text path stores characters;
- last_depth stays 0 for legacy (non-VBE) graphics modes, because
vga_get_bpp() only reports a depth when VBE is enabled, so the
"s->last_depth" term that normally forces a resize after a graphics
frame does not fire.
So a graphics frame that shrinks the console surface (e.g. 80x25
pixels) followed by a text frame with matching character geometry
(80x25 chars) skips the resize, and the glyph loop then paints
width*cw x height*cheight pixels into the smaller surface, out of
bounds, with guest-controlled (DAC palette) values, on every display
refresh.
Separate the geometry cache per renderer: text paths (vga_draw_text,
vga_update_text, and the text handling in vga_invalidate_display /
vga_common_reset) now only manipulate last_text_{width,height}, in
characters; last_{width,height} become graphics-only, in pixels.
Additionally, make the text path compare the pixel size it is about
to paint against the console surface's actual dimensions. The
surface check is the load-bearing term: caches in either unit cannot
see the other renderer swapping the surface, the surface can.
Fixes: CVE-2026-77913
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4215
Cc: qemu-stable@nongnu.org
Signed-off-by: Warisjeet Singh (sin99xx) <sinxx198@gmail.com>
Message-ID: <vga-v3-20260824.sinxx198@gmail.com>
---
hw/display/vga.c | 37 ++++++++++++++++++++++---------------
hw/display/vga_int.h | 3 ++-
2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/hw/display/vga.c b/hw/display/vga.c
index da0c331486eb..cb0e28b79b6a 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1241,7 +1241,10 @@ static void vga_draw_text(VGACommonState *s, int full_update)
return;
}
- if (width != s->last_width || height != s->last_height ||
+ if (surface == NULL ||
+ surface_width(surface) != width * cw ||
+ surface_height(surface) != height * cheight ||
+ width != s->last_text_width || height != s->last_text_height ||
cw != s->last_cw || cheight != s->last_ch || s->last_depth) {
s->last_scr_width = width * cw;
s->last_scr_height = height * cheight;
@@ -1249,8 +1252,8 @@ static void vga_draw_text(VGACommonState *s, int full_update)
surface = qemu_console_surface(s->con);
qemu_console_text_resize(s->con, width, height);
s->last_depth = 0;
- s->last_width = width;
- s->last_height = height;
+ s->last_text_width = width;
+ s->last_text_height = height;
s->last_ch = cheight;
s->last_cw = cw;
full_update = 1;
@@ -1845,6 +1848,8 @@ static void vga_invalidate_display(void *opaque)
s->last_width = -1;
s->last_height = -1;
+ s->last_text_width = -1;
+ s->last_text_height = -1;
}
void vga_common_reset(VGACommonState *s)
@@ -1887,6 +1892,8 @@ void vga_common_reset(VGACommonState *s)
s->last_ch = 0;
s->last_width = 0;
s->last_height = 0;
+ s->last_text_width = 0;
+ s->last_text_height = 0;
s->last_scr_width = 0;
s->last_scr_height = 0;
s->cursor_start = 0;
@@ -1938,8 +1945,8 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
s->graphic_mode = graphic_mode;
full_update = 1;
}
- if (s->last_width == -1) {
- s->last_width = 0;
+ if (s->last_text_width == -1) {
+ s->last_text_width = 0;
full_update = 1;
}
@@ -1978,15 +1985,15 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
break;
}
- if (width != s->last_width || height != s->last_height ||
+ if (width != s->last_text_width || height != s->last_text_height ||
cw != s->last_cw || cheight != s->last_ch) {
s->last_scr_width = width * cw;
s->last_scr_height = height * cheight;
qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height);
qemu_console_text_resize(s->con, width, height);
s->last_depth = 0;
- s->last_width = width;
- s->last_height = height;
+ s->last_text_width = width;
+ s->last_text_height = height;
s->last_ch = cheight;
s->last_cw = cw;
full_update = 1;
@@ -2071,22 +2078,22 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
}
/* Display a message */
- s->last_width = 60;
- s->last_height = height = 3;
+ s->last_text_width = 60;
+ s->last_text_height = height = 3;
qemu_console_text_set_cursor(s->con, -1, -1);
- qemu_console_text_resize(s->con, s->last_width, height);
+ qemu_console_text_resize(s->con, s->last_text_width, height);
- for (dst = chardata, i = 0; i < s->last_width * height; i ++)
+ for (dst = chardata, i = 0; i < s->last_text_width * height; i ++)
*dst++ = ' ';
size = strlen(msg_buffer);
- width = (s->last_width - size) / 2;
- dst = chardata + s->last_width + width;
+ width = (s->last_text_width - size) / 2;
+ dst = chardata + s->last_text_width + width;
for (i = 0; i < size; i ++)
*dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
QEMU_COLOR_BLACK, 1);
- qemu_console_text_update(s->con, 0, 0, s->last_width, height);
+ qemu_console_text_update(s->con, 0, 0, s->last_text_width, height);
}
static uint64_t vga_mem_read(void *opaque, hwaddr addr,
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 5664317ecd6d..ca69ae981521 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -122,7 +122,8 @@ typedef struct VGACommonState {
uint32_t plane_updated;
uint32_t last_line_offset;
uint8_t last_cw, last_ch;
- uint32_t last_width, last_height; /* in chars or pixels */
+ uint32_t last_width, last_height; /* in pixels (graphics renderer) */
+ uint32_t last_text_width, last_text_height; /* in chars (text renderer) */
uint32_t last_scr_width, last_scr_height; /* in pixels */
uint32_t last_depth; /* in bits */
bool last_byteswap;
--
2.55.0.543.g5ebe2ebe4ea8
next prev parent reply other threads:[~2026-08-25 11:30 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 11:20 [GIT PULL 00/19] Various fixes Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 01/19] hw/misc: fix trace-events Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 02/19] migration/multifd: fix Error leak in multifd_recv_terminate_threads() Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 03/19] hw/core/machine: fix fdt memory leak Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 04/19] hw/display/qxl: validate primary surface stride against width Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 05/19] virtio-gpu: use g_try_malloc to avoid guest-triggered abort Marc-André Lureau
2026-09-03 19:36 ` Peter Maydell
2026-09-03 20:53 ` Marc-André Lureau
2026-09-10 11:19 ` Peter Maydell
2026-08-25 11:20 ` [GIT PULL 06/19] crypto: fix build against nettle >= 4 Marc-André Lureau
2026-08-25 11:54 ` Daniel P. Berrangé
2026-08-25 11:20 ` [GIT PULL 07/19] tests: tag slow tests with 'slow' suite for easy filtering Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 08/19] ui/egl: fix render node cleanup order Marc-André Lureau
2026-08-25 11:20 ` [GIT PULL 09/19] ui/egl: fix qemu_egl_display type Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 10/19] tests/functional: fix pylint false positives for cv2 module Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 11/19] chardev: Don't unregister yank upon async path connection failure Marc-André Lureau
2026-08-25 11:21 ` Marc-André Lureau [this message]
2026-08-25 11:21 ` [GIT PULL 13/19] hw/display/virtio-gpu: Avoid creating empty udmabuf Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 14/19] hw/display/virtio-gpu: Avoid mmap() for empty blob Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 15/19] hw/display/virtio-gpu: Propagate udmabuf errors Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 16/19] hw/display/virtio-gpu: Check cursor data presence Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 17/19] hw/display/virtio-gpu: Validate resource per command Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 18/19] hw/input/ps2: answer unknown mouse commands with a resend Marc-André Lureau
2026-08-25 11:21 ` [GIT PULL 19/19] hw/input/ps2: say why unknown keyboard commands draw " Marc-André Lureau
2026-08-25 18:40 ` [GIT PULL 00/19] Various fixes Richard Henderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260825-fixes-v1-12-c59e8a620836@redhat.com \
--to=marcandre.lureau@redhat.com \
--cc=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.