From 8034a203bbff0aa3b53f2946dc58e409bd7246c9 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 20 Jan 2022 15:03:31 -0600
Subject: [PATCH] Fix: scheduler: avoid memory leak when displaying clones

Previously, pe__clone_default() unconditionally created a hash table for
stopped instances, but didn't free it in every code path.

Now, only create the table when we have something to put in it and might
actually use it, and ensure it always gets freed.
---
 lib/pengine/clone.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c
index 742e2920b0..920a04c32c 100644
--- a/lib/pengine/clone.c
+++ b/lib/pengine/clone.c
@@ -761,7 +761,7 @@ pe__clone_default(pcmk__output_t *out, va_list args)
     GList *only_node = va_arg(args, GList *);
     GList *only_rsc = va_arg(args, GList *);
 
-    GHashTable *stopped = pcmk__strkey_table(free, free);
+    GHashTable *stopped = NULL;
 
     char *list_text = NULL;
     size_t list_text_len = 0;
@@ -818,7 +818,11 @@ pe__clone_default(pcmk__output_t *out, va_list args)
         } else if (partially_active == FALSE) {
             // List stopped instances when requested (except orphans)
             if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
+                && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
                 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
+                if (stopped == NULL) {
+                    stopped = pcmk__strkey_table(free, free);
+                }
                 g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped"));
             }
 
@@ -873,7 +877,6 @@ pe__clone_default(pcmk__output_t *out, va_list args)
     }
 
     if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
-        g_hash_table_destroy(stopped);
         PCMK__OUTPUT_LIST_FOOTER(out, rc);
         return pcmk_rc_ok;
     }
@@ -948,8 +951,10 @@ pe__clone_default(pcmk__output_t *out, va_list args)
             GList *list = g_hash_table_get_values(rsc->allowed_nodes);
 
             /* Custom stopped table for non-unique clones */
-            g_hash_table_destroy(stopped);
-            stopped = pcmk__strkey_table(free, free);
+            if (stopped != NULL) {
+                g_hash_table_destroy(stopped);
+                stopped = NULL;
+            }
 
             if (list == NULL) {
                 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
@@ -972,6 +977,9 @@ pe__clone_default(pcmk__output_t *out, va_list args)
                         state = "Stopped (disabled)";
                     }
 
+                    if (stopped == NULL) {
+                        stopped = pcmk__strkey_table(free, free);
+                    }
                     if (probe_op != NULL) {
                         int rc;
 
@@ -987,7 +995,7 @@ pe__clone_default(pcmk__output_t *out, va_list args)
             g_list_free(list);
         }
 
-        if (g_hash_table_size(stopped) > 0) {
+        if (stopped != NULL) {
             GList *list = sorted_hash_table_values(stopped);
 
             clone_header(out, &rc, rsc, clone_data);
-- 
2.27.0

