Commit 1f9c9387b0 for qemu.org

commit 1f9c9387b00183865121d01ae09de0423a840410
Author: Marc-André Lureau <marcandre.lureau@redhat.com>
Date:   Tue Feb 24 16:05:58 2026 +0100

    ui/vnc: fix vnc_display_init() leak on failure

    Do not add the display state to the vnc list, if the initialization
    failed. Add vnc_display_free(), to free the display state and associated
    data in such case. The function is meant to be public and reused in the
    following changes.

    Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
    Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

diff --git a/ui/vnc.c b/ui/vnc.c
index d5d92c0d99..37ebd1a534 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3424,6 +3424,8 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
 }

+static void vnc_display_free(VncDisplay *vd);
+
 void vnc_display_init(const char *id, Error **errp)
 {
     VncDisplay *vd;
@@ -3433,8 +3435,9 @@ void vnc_display_init(const char *id, Error **errp)
     }
     vd = g_malloc0(sizeof(*vd));

+    qemu_mutex_init(&vd->mutex);
     vd->id = g_strdup(id);
-    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
+    vd->dcl.ops = &dcl_ops;

     QTAILQ_INIT(&vd->clients);
     vd->expires = TIME_MAX;
@@ -3448,22 +3451,22 @@ void vnc_display_init(const char *id, Error **errp)
     }

     if (!vd->kbd_layout) {
+        vnc_display_free(vd);
         return;
     }

     vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
     vd->connections_limit = 32;

-    qemu_mutex_init(&vd->mutex);
     vnc_start_worker_thread();

-    vd->dcl.ops = &dcl_ops;
     register_displaychangelistener(&vd->dcl);
     vd->kbd = qkbd_state_init(vd->dcl.con);
     vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
         &vmstate_change_handler, vd);
-}

+    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
+}

 static void vnc_display_close(VncDisplay *vd)
 {
@@ -3507,6 +3510,28 @@ static void vnc_display_close(VncDisplay *vd)
 #endif
 }

+static void vnc_display_free(VncDisplay *vd)
+{
+    if (!vd) {
+        return;
+    }
+
+    assert(QTAILQ_EMPTY(&vd->clients));
+
+    vnc_display_close(vd);
+    unregister_displaychangelistener(&vd->dcl);
+    qkbd_state_free(vd->kbd);
+    qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
+    kbd_layout_free(vd->kbd_layout);
+    qemu_mutex_destroy(&vd->mutex);
+    if (QTAILQ_IN_USE(vd, next)) {
+        QTAILQ_REMOVE(&vnc_displays, vd, next);
+    }
+    g_free(vd->id);
+    g_free(vd);
+}
+
+
 int vnc_display_password(const char *id, const char *password, Error **errp)
 {
     VncDisplay *vd = vnc_display_find(id);