[PATCH] hw/display/qxl: fix heap buffer overflow in qxl_blit

marcandre.lureau@redhat.com posted 1 patch 1 day, 10 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260710134450.2314872-1-marcandre.lureau@redhat.com
hw/display/qxl-render.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
[PATCH] hw/display/qxl: fix heap buffer overflow in qxl_blit
Posted by marcandre.lureau@redhat.com 1 day, 10 hours ago
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