Commit 3cae0b46be for qemu.org

commit 3cae0b46be5416b26039df5259ffc8fcf2989516
Author: Marc-André Lureau <marcandre.lureau@redhat.com>
Date:   Fri Mar 13 20:54:47 2026 +0400

    ui/vnc-jobs: fix VncRectEntry leak on job cleanup

    When a VncJob is freed, its associated VncRectEntry list must also be
    freed. Previously, vnc_job_push() and the disconnected path in
    vnc_worker_thread_loop() called g_free(job) directly, leaking all
    VncRectEntry allocations.

    Introduce vnc_job_free() which iterates and frees the rectangle entries
    before freeing the job itself, and use it in both paths.

    Also add QLIST_REMOVE() in the worker loop before g_free(entry), so
    that entries processed during normal operation are properly unlinked.
    Without this, vnc_job_free() would iterate dangling pointers to
    already-freed entries, causing use-after-free.

    Fixes: bd023f953e5e ("vnc: threaded VNC server")
    Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
    Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index b296d19e08..ca625da6d0 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -107,11 +107,25 @@ int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
     return 1;
 }

+static void vnc_job_free(VncJob *job)
+{
+    VncRectEntry *entry, *tmp;
+
+    if (!job) {
+        return;
+    }
+    QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+        /* no need for QLIST_REMOVE(entry, next) */
+        g_free(entry);
+    }
+    g_free(job);
+}
+
 void vnc_job_push(VncJob *job)
 {
     vnc_lock_queue(queue);
     if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
-        g_free(job);
+        vnc_job_free(job);
     } else {
         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
         qemu_cond_broadcast(&queue->cond);
@@ -296,6 +310,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
                 n_rectangles += n;
             }
         }
+        QLIST_REMOVE(entry, next);
         g_free(entry);
     }
     trace_vnc_job_nrects(&vs, job, n_rectangles);
@@ -324,7 +339,7 @@ disconnected:
     QTAILQ_REMOVE(&queue->jobs, job, next);
     vnc_unlock_queue(queue);
     qemu_cond_broadcast(&queue->cond);
-    g_free(job);
+    vnc_job_free(job);
     vs.magic = 0;
     return 0;
 }