@samitouri / QOSamiQemu / commits / e650e4fe0f

ui/vnc: fix out-of-bounds write in lossy refresh dirty marking

vnc_refresh_lossy_rect() marks a full VNC_STAT_RECT (64) rows of the dirty bitmap when refreshing a lossy tile. When the display height is not a multiple of VNC_STAT_RECT, the last tile row is a partial tile and the loop writes past the end of vs->dirty[VNC_MAX_HEIGHT]. For example, with a 2160-pixel-high display (VNC_MAX_HEIGHT), the last stat tile starts at y=2112. The unconditional 64-row loop writes rows 2112..2175, overflowing 16 rows (640 bytes) past the dirty bitmap into subsequent VncState fields. Fix by passing the effective display height into vnc_refresh_lossy_rect() and clamping the inner loop. Fixes: CVE-2026-61475 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3935 Reported-by: "Vulnerability Report" <vr@darknavy.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Jul 9, 2026 at 17:42 UTC e650e4fe0fb35b7a8ec9fc04e00346c02640bd58
1 file changed +4 -4
ui/vnc.c
+4 -4
@@ -3000,18 +3000,18 @@ void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h)
3000 }
3001 }
3002
3003 -static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
3003 +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y,
3004 + int height)
3005 {
3006 VncState *vs;
3007 int sty = y / VNC_STAT_RECT;
3008 int stx = x / VNC_STAT_RECT;
3009 int has_dirty = 0;
3009 - int height = MIN(pixman_image_get_height(vd->guest.fb),
3010 - pixman_image_get_height(vd->server));
3010 int rows;
3011
3012 y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT);
3013 x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
3014 + rows = MIN(VNC_STAT_RECT, height - y);
3015
3016 rows = MIN(VNC_STAT_RECT, height - y);
3017 if (rows <= 0) {
@@ -3083,7 +3083,7 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv)
3083
3084 if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) {
3085 rect->freq = 0;
3086 - has_dirty += vnc_refresh_lossy_rect(vd, x, y);
3086 + has_dirty += vnc_refresh_lossy_rect(vd, x, y, height);
3087 memset(rect->times, 0, sizeof (rect->times));
3088 continue ;
3089 }