[PATCH v2] hw/display/virtio-gpu: fix use-after-free on blob detach backing

[email protected]
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
From: Marc-André Lureau <[email protected]>

virtio_gpu_cleanup_mapping() tears down the udmabuf mapping (or, for the
small-iov case, the guest memory mapping) via virtio_gpu_fini_udmabuf(),
but leaves res->blob pointing at it afterwards. Once a blob resource
goes through RESOURCE_DETACH_BACKING, res->blob is left dangling while
res->blob_size is still non-zero.

virtio_gpu_update_cursor_data() only checks res->blob_size before
copying from res->blob, so UPDATE_CURSOR on a detached blob resource
makes QEMU memcpy from freed memory.

virtio_gpu_do_set_scanout() also creates scanout->ds over
res->blob + fb->offset. RESOURCE_DETACH_BACKING leaves that surface
installed, so a subsequent display refresh can still read the backing
after virtio_gpu_fini_udmabuf() has unmapped it.

Disable any active scanouts referencing the resource before tearing down
its backing, clear res->blob afterwards, and check it before
dereferencing in virtio_gpu_update_cursor_data().

Fixes: CVE-2026-66020
Fixes: bdd53f739273 ("virtio-gpu: Update cursor data using blob")
Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3881
Reported-by: 章鱼哥@aipy (www.aipyaipy.com)
Reported-by: swing <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
---
v3:
 - only disable blob-based scanouts on detach_backing
---
 hw/display/virtio-gpu.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 574e68992925..9bacd38b4521 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -63,8 +63,16 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
         }
         data = pixman_image_get_data(res->image);
     } else {
+        if (!res->blob) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d has no blob\n",
+                          __func__, resource_id);
+            return;
+        }
         if (res->blob_size < (s->current_cursor->width *
                               s->current_cursor->height * 4)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: blob size too small for resource %d\n",
+                          __func__, resource_id);
             return;
         }
         data = res->blob;
@@ -974,6 +982,7 @@ void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
 
     if (res->blob) {
         virtio_gpu_fini_udmabuf(g, res);
+        res->blob = NULL;
     }
 }
 
@@ -1039,6 +1048,15 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g,
     if (!res) {
         return;
     }
+
+    if (res->blob) {
+        for (int i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+            if (res->scanout_bitmask & (1 << i)) {
+                virtio_gpu_disable_scanout(g, i);
+            }
+        }
+    }
+
     virtio_gpu_cleanup_mapping(g, res);
 }
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.