From: Marc-André Lureau <marcandre.lureau@redhat.com>
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>
Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com>
---
ui/vnc.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/ui/vnc.c b/ui/vnc.c
index b2b69923b75..dfc8262d42a 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2998,15 +2998,18 @@ void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h)
}
}
-static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
+static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y,
+ int height)
{
VncState *vs;
int sty = y / VNC_STAT_RECT;
int stx = x / VNC_STAT_RECT;
int has_dirty = 0;
+ int rows;
y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT);
x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
+ rows = MIN(VNC_STAT_RECT, height - y);
QTAILQ_FOREACH(vs, &vd->clients, next) {
VncConnection *vc = container_of(vs, VncConnection, vs);
@@ -3022,7 +3025,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
}
vc->worker.lossy_rect[sty][stx] = 0;
- for (j = 0; j < VNC_STAT_RECT; ++j) {
+ for (j = 0; j < rows; ++j) {
bitmap_set(vs->dirty[y + j],
x / VNC_DIRTY_PIXELS_PER_BIT,
VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);
@@ -3073,7 +3076,7 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv)
if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) {
rect->freq = 0;
- has_dirty += vnc_refresh_lossy_rect(vd, x, y);
+ has_dirty += vnc_refresh_lossy_rect(vd, x, y, height);
memset(rect->times, 0, sizeof (rect->times));
continue ;
}
--
2.55.0