From: Marc-André Lureau <[email protected]> qxl_blit() used the guest-controlled abs_stride to walk the destination display surface pointer. When the guest sets a negative stride, the display surface is allocated with qemu_create_displaysurface() using width-based stride, but abs_stride can be much larger (e.g. 65536 for width=64). This causes dst to advance past the allocated surface buffer, resulting in an out-of-bounds heap write with attacker-controlled bytes.
Use surface_stride() for the destination pointer instead, which returns the actual stride of the allocated display surface. When stride > 0 the surface is created with abs_stride so behavior is unchanged. Fixes: CVE-2026-15251 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3813 Reported-by: Pulkit Singh Singaria (x3ero0) Signed-off-by: Marc-André Lureau <[email protected]> --- hw/display/qxl-render.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 4799c9e8bef..3f922f5c170 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -30,6 +30,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) uint8_t *dst = surface_data(surface); uint8_t *src; int len, i; + int dst_stride = surface_stride(surface); if (!surface_is_allocated(surface)) { return; @@ -45,14 +46,14 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) } else { src += rect->top * qxl->guest_primary.abs_stride; } - dst += rect->top * qxl->guest_primary.abs_stride; + dst += rect->top * dst_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); - dst += qxl->guest_primary.abs_stride; + dst += dst_stride; src += qxl->guest_primary.qxl_stride; } } -- 2.55.0
