File bsc#1209640-0001-Fix-controller-avoid-use-after-free-when-disconnecti.patch of Package pacemaker.34782

From db7f6f3e99524a613758747ffcedd29fc067bbc1 Mon Sep 17 00:00:00 2001
From: "Gao,Yan" <ygao@suse.com>
Date: Thu, 23 Mar 2023 19:05:13 +0100
Subject: [PATCH] Fix: controller: avoid use-after-free when disconnecting
 proxy IPCs during shutdown

Previously internal_lrm_state_destroy() directly freed and removed any
relevant proxy entries from proxy_table, which could potentially leave
the proxy IPCs connected and cause use-after-free when
g_main_context_dispatch() -> ... -> remote_proxy_disconnected() was
later called in crmd_exit().

The solution is to make sure any connected proxies get disconnected. So
that remote_proxy_disconnected() will be called and as well remove the
entries from proxy_table.
---
 daemons/controld/controld_execd_state.c | 86 ++++++++++++++-----------
 1 file changed, 47 insertions(+), 39 deletions(-)

Index: pacemaker-2.1.5+20221208.a3f44794f/daemons/controld/controld_execd_state.c
===================================================================
--- pacemaker-2.1.5+20221208.a3f44794f.orig/daemons/controld/controld_execd_state.c
+++ pacemaker-2.1.5+20221208.a3f44794f/daemons/controld/controld_execd_state.c
@@ -150,6 +150,45 @@ remote_proxy_remove_by_node(gpointer key
     return FALSE;
 }
 
+static remote_proxy_t *
+find_connected_proxy_by_node(const char * node_name)
+{
+    GHashTableIter gIter;
+    remote_proxy_t *proxy = NULL;
+
+    CRM_CHECK(proxy_table != NULL, return NULL);
+
+    g_hash_table_iter_init(&gIter, proxy_table);
+
+    while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) &proxy)) {
+        if (proxy->source
+            && pcmk__str_eq(node_name, proxy->node_name, pcmk__str_casei)) {
+            return proxy;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+remote_proxy_disconnect_by_node(const char * node_name)
+{
+    remote_proxy_t *proxy = NULL;
+
+    CRM_CHECK(proxy_table != NULL, return);
+
+    while ((proxy = find_connected_proxy_by_node(node_name)) != NULL) {
+        /* mainloop_del_ipc_client() eventually calls remote_proxy_disconnected()
+         * , which removes the entry from proxy_table.
+         * Do not do this in a g_hash_table_iter_next() loop. */
+        if (proxy->source) {
+            mainloop_del_ipc_client(proxy->source);
+        }
+    }
+
+    return;
+}
+
 static void
 internal_lrm_state_destroy(gpointer data)
 {
@@ -159,7 +198,16 @@ internal_lrm_state_destroy(gpointer data
         return;
     }
 
-    crm_trace("Destroying proxy table %s with %d members", lrm_state->node_name, g_hash_table_size(proxy_table));
+    /* Rather than directly remove the recorded proxy entries from proxy_table,
+     * make sure any connected proxies get disconnected. So that
+     * remote_proxy_disconnected() will be called and as well remove the
+     * entries from proxy_table.
+     */
+    remote_proxy_disconnect_by_node(lrm_state->node_name);
+
+    crm_trace("Destroying proxy table %s with %u members",
+              lrm_state->node_name, g_hash_table_size(proxy_table));
+    // Just in case there's still any leftovers in proxy_table
     g_hash_table_foreach_remove(proxy_table, remote_proxy_remove_by_node, (char *) lrm_state->node_name);
     remote_ra_cleanup(lrm_state);
     lrmd_api_delete(lrm_state->conn);
@@ -277,45 +325,6 @@ lrm_state_get_list(void)
     return g_hash_table_get_values(lrm_state_table);
 }
 
-static remote_proxy_t *
-find_connected_proxy_by_node(const char * node_name)
-{
-    GHashTableIter gIter;
-    remote_proxy_t *proxy = NULL;
-
-    CRM_CHECK(proxy_table != NULL, return NULL);
-
-    g_hash_table_iter_init(&gIter, proxy_table);
-
-    while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) &proxy)) {
-        if (proxy->source
-            && pcmk__str_eq(node_name, proxy->node_name, pcmk__str_casei)) {
-            return proxy;
-        }
-    }
-
-    return NULL;
-}
-
-static void
-remote_proxy_disconnect_by_node(const char * node_name)
-{
-    remote_proxy_t *proxy = NULL;
-
-    CRM_CHECK(proxy_table != NULL, return);
-
-    while ((proxy = find_connected_proxy_by_node(node_name)) != NULL) {
-        /* mainloop_del_ipc_client() eventually calls remote_proxy_disconnected()
-         * , which removes the entry from proxy_table.
-         * Do not do this in a g_hash_table_iter_next() loop. */
-        if (proxy->source) {
-            mainloop_del_ipc_client(proxy->source);
-        }
-    }
-
-    return;
-}
-
 void
 lrm_state_disconnect_only(lrm_state_t * lrm_state)
 {
openSUSE Build Service is sponsored by