hw/display/g364fb: unvalidated top_of_screen leads to OOB read and host crash
Summary
The G364 framebuffer device allows a guest to set the top_of_screen register to any 32-bit value without validation against the VRAM buffer size. The display update function g364fb_draw_graphic8() computes a VRAM read pointer as memory_region_get_ram_ptr(&s->mem_vram) + s->top_of_screen, reading past the allocated VRAM buffer into unmapped or adjacent host memory. This causes a host QEMU process crash (SIGSEGV) when the OOB address is unmapped.
The developers flagged this exact issue with an inline comment /* XXX: out of range in vram? */ but never added the check.
CWE: CWE-125 (Out-of-bounds Read)
CVSS 3.1: 6.5 (AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H)
Verified at: HEAD commit 3377670e
Machine: MIPS Jazz (-machine jazz)
Root Cause
In g364fb_ctrl_write() (g364fb.c:405-407), top_of_screen is set from the guest value without bounds check:
case REG_TOP:
s->top_of_screen = val;
g364fb_invalidate_display(s);
break;The display draw function (g364fb.c:128):
vram = memory_region_get_ram_ptr(&s->mem_vram) + s->top_of_screen;
/* XXX: out of range in vram? */VRAM size is 8 MiB default (configurable). No validation that top_of_screen < vram_size.
Trigger
- Guest sets
top_of_screentovram_sizeor larger via MMIO register write at offset REG_TOP (0x00400) - Guest writes any value to VRAM offset 0 (marks page 0 dirty)
- Host display update callback fires (periodic, automatic)
g364fb_draw_graphic8reads from OOB pointer, crashes or leaks data
The width and height registers are also unvalidated, allowing resource exhaustion via qemu_console_resize() with guest-controlled dimensions.
Impact
Host QEMU process crash (SIGSEGV). Limited info leak if OOB bytes are used as palette indices rendered on the host display. MIPS Jazz machine only.
Proposed Fix
Clamp top_of_screen to vram_size at the register write handler, and add a safety check in the draw function:
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -126,7 +126,9 @@ static void g364fb_draw_graphic8(G364State *s)
vram = memory_region_get_ram_ptr(&s->mem_vram) + s->top_of_screen;
- /* XXX: out of range in vram? */
+ if (s->top_of_screen >= s->vram_size) {
+ return;
+ }
data_display = dd = surface_data(surface);
@@ -405,6 +407,9 @@ static void g364fb_ctrl_write(...)
case REG_TOP:
- s->top_of_screen = val;
+ s->top_of_screen = val;
+ if (s->top_of_screen >= s->vram_size) {
+ s->top_of_screen = 0;
+ }
g364fb_invalidate_display(s);