virtio_gpu_resource_detach_backing() releases a resource's mapping
through virtio_gpu_cleanup_mapping(), which for a blob resource unmaps
and releases the udmabuf backing. Two lifetime problems follow:
1. A blob resource may currently back a scanout: do_set_scanout()
creates a pixman surface that aliases res->blob memory
(scanout->ds -> data inside res->blob). detach does not drop those
scanouts, so after cleanup_mapping() releases the backing, a later
display refresh reads freed memory (use-after-free). Drop every
scanout that still references the resource before unmapping,
mirroring what virtio_gpu_resource_destroy() already does before it
frees a resource.
2. cleanup_mapping() never clears res->blob after fini_udmabuf(), so
the resource keeps a dangling pointer to released memory while it
stays on reslist (detach does not remove the resource; the guest can
re-attach backing later). NULL it out so code that consults
res->blob (e.g. cursor/scanout paths) cannot touch freed memory.
RFC: this overlaps the upstream virtio-gpu blob/DETACH_BACKING fix
series (CVE-2026-66020 family); confirm it does not duplicate an
in-flight version before applying.
Signed-off-by: Hongyan Xu <getshell@seu.edu.cn>
---
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 55a1c7f80f..d0d15deb28 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1030,6 +1030,7 @@ void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
if (res->blob) {
virtio_gpu_fini_udmabuf(g, res);
+ res->blob = NULL;
}
}
@@ -1096,6 +1097,23 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g,
if (!res) {
return;
}
+
+ /*
+ * A blob resource may be backing a scanout: the scanout's surface
+ * aliases res->blob memory (do_set_scanout). cleanup_mapping()
+ * below unmaps/releases that memory, so drop every scanout that
+ * still references this resource first, mirroring what
+ * virtio_gpu_resource_destroy() does before it frees a resource.
+ */
+ if (res->scanout_bitmask) {
+ int i;
+
+ for (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.50.1.windows.1