@samitouri / QOSamiQemu / commits / 3543c2b855

ui/vnc: fix OOB write in vnc_refresh_lossy_rect

vnc_refresh_lossy_rect() always marks a full VNC_STAT_RECT (64) rows as dirty when refreshing a lossy tile. When the display height is not a multiple of VNC_STAT_RECT (e.g. VNC_MAX_HEIGHT = 2160), the bottom tile is partial -- the last tile at y=2112 has only 48 valid rows. The unclamped loop writes to vs->dirty[2160..2175], past the end of the VNC_MAX_HEIGHT-sized array. Clamp the row count to the actual surface height so partial bottom tiles only mark valid dirty bitmap entries. Fixes: CVE-2026-48002 Fixes: 7d964c9d2fc6 ("vnc: refresh lossy rect after a given timeout") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3950 Reported-by: huntr bubble Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Jul 6, 2026 at 12:45 UTC 3543c2b855cc8cd25a5dbf05564a47ba42f45fad
1 file changed +9 -1
ui/vnc.c
+9 -1
@@ -3004,10 +3004,18 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
3004 int sty = y / VNC_STAT_RECT;
3005 int stx = x / VNC_STAT_RECT;
3006 int has_dirty = 0;
3007 + int height = MIN(pixman_image_get_height(vd->guest.fb),
3008 + pixman_image_get_height(vd->server));
3009 + int rows;
3010
3011 y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT);
3012 x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
3013
3014 + rows = MIN(VNC_STAT_RECT, height - y);
3015 + if (rows <= 0) {
3016 + return 0;
3017 + }
3018 +
3019 QTAILQ_FOREACH(vs, &vd->clients, next) {
3020 VncConnection *vc = container_of(vs, VncConnection, vs);
3021 int j;
@@ -3022,7 +3030,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
3030 }
3031
3032 vc->worker.lossy_rect[sty][stx] = 0;
3025 - for (j = 0; j < VNC_STAT_RECT; ++j) {
3033 + for (j = 0; j < rows; ++j) {
3034 bitmap_set(vs->dirty[y + j],
3035 x / VNC_DIRTY_PIXELS_PER_BIT,
3036 VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);