From: Marc-André Lureau <marcandre.lureau@redhat.com>
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 <marcandre.lureau@redhat.com>
---
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