Blob Blame History Raw
From dd521e724c4c2d4f074ffcf30a9e7a844fcfb7d2 Mon Sep 17 00:00:00 2001
From: Klaus Wenninger <klaus.wenninger@aon.at>
Date: Sat, 18 May 2019 06:17:36 +0200
Subject: [PATCH] Fix: fence-lib: avoid use-after-free on early failure return

Bailing out in case of non-existant fence-agent is such a case.
---
 fencing/commands.c             | 30 +++++++++++++++++++++---------
 include/crm/fencing/internal.h |  5 +++--
 lib/fencing/st_client.c        | 28 +++++++++++++++++++++-------
 3 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/fencing/commands.c b/fencing/commands.c
index af5324a..d47a5ea 100644
--- a/fencing/commands.c
+++ b/fencing/commands.c
@@ -107,6 +107,7 @@ typedef struct async_command_s {
     int last_timeout_signo;
 
     stonith_device_t *active_on;
+    stonith_device_t *activating_on;
 } async_command_t;
 
 static xmlNode *stonith_construct_async_reply(async_command_t * cmd, const char *output,
@@ -301,6 +302,19 @@ get_active_cmds(stonith_device_t * device)
     return counter;
 }
 
+static void
+fork_cb(GPid pid, gpointer user_data)
+{
+    async_command_t *cmd = (async_command_t *) user_data;
+    stonith_device_t * device = cmd->activating_on;
+
+    crm_debug("Operation %s%s%s on %s now running with pid=%d, timeout=%ds",
+                  cmd->action, cmd->victim ? " for node " : "", cmd->victim ? cmd->victim : "",
+                  device->id, pid, cmd->timeout);
+    cmd->active_on = device;
+    cmd->activating_on = NULL;
+}
+
 static gboolean
 stonith_device_execute(stonith_device_t * device)
 {
@@ -387,19 +401,17 @@ stonith_device_execute(stonith_device_t * device)
                                    cmd->victim_nodeid,
                                    cmd->timeout, device->params, device->aliases);
 
-    /* for async exec, exec_rc is pid if positive and error code if negative/zero */
-    exec_rc = stonith_action_execute_async(action, (void *)cmd, cmd->done_cb);
-
-    if (exec_rc > 0) {
-        crm_debug("Operation %s%s%s on %s now running with pid=%d, timeout=%ds",
-                  cmd->action, cmd->victim ? " for node " : "", cmd->victim ? cmd->victim : "",
-                  device->id, exec_rc, cmd->timeout);
-        cmd->active_on = device;
+    /* for async exec, exec_rc is negative for early error exit
+       otherwise handling of success/errors is done via callbacks */
+    cmd->activating_on = device;
+    exec_rc = stonith_action_execute_async(action, (void *)cmd,
+                                           cmd->done_cb, fork_cb);
 
-    } else {
+    if (exec_rc < 0) {
         crm_warn("Operation %s%s%s on %s failed: %s (%d)",
                  cmd->action, cmd->victim ? " for node " : "", cmd->victim ? cmd->victim : "",
                  device->id, pcmk_strerror(exec_rc), exec_rc);
+        cmd->activating_on = NULL;
         cmd->done_cb(0, exec_rc, NULL, cmd);
     }
     return TRUE;
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
index 90673bb..24df230 100644
--- a/include/crm/fencing/internal.h
+++ b/include/crm/fencing/internal.h
@@ -24,11 +24,12 @@ void stonith__destroy_action(stonith_action_t *action);
 void stonith__action_result(stonith_action_t *action, int *rc, char **output,
                             char **error_output);
 
-GPid
+int
 stonith_action_execute_async(stonith_action_t * action,
                              void *userdata,
                              void (*done) (GPid pid, int rc, const char *output,
-                                           gpointer user_data));
+                                           gpointer user_data),
+                             void (*fork_cb) (GPid pid, gpointer user_data));
 
 int stonith__execute(stonith_action_t *action);
 
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
index 0c1eadc..c38f356 100644
--- a/lib/fencing/st_client.c
+++ b/lib/fencing/st_client.c
@@ -54,6 +54,7 @@ struct stonith_action_s {
     int async;
     void *userdata;
     void (*done_cb) (GPid pid, gint status, const char *output, gpointer user_data);
+    void (*fork_cb) (GPid pid, gpointer user_data);
 
     svc_action_t *svc_action;
 
@@ -835,6 +836,10 @@ stonith_action_async_forked(svc_action_t *svc_action)
     action->pid = svc_action->pid;
     action->svc_action = svc_action;
 
+    if (action->fork_cb) {
+        (action->fork_cb) (svc_action->pid, action->userdata);
+    }
+
     crm_trace("Child process %d performing action '%s' successfully forked",
               action->pid, action->action);
 }
@@ -916,25 +921,34 @@ internal_stonith_action_execute(stonith_action_t * action)
     return rc;
 }
 
-GPid
+/*!
+ * \internal
+ * \brief Kick off execution of an async stonith action
+ *
+ * \param[in,out] action        Action to be executed
+ * \param[in,out] userdata      Datapointer to be passed to callbacks
+ * \param[in]     done          Callback to notify action has failed/succeeded
+ * \param[in]     fork_callback Callback to notify successful fork of child
+ *
+ * \return pcmk_ok if ownership of action has been taken, -errno otherwise
+ */
+int
 stonith_action_execute_async(stonith_action_t * action,
                              void *userdata,
                              void (*done) (GPid pid, int rc, const char *output,
-                                           gpointer user_data))
+                                           gpointer user_data),
+                             void (*fork_cb) (GPid pid, gpointer user_data))
 {
-    int rc = 0;
-
     if (!action) {
         return -1;
     }
 
     action->userdata = userdata;
     action->done_cb = done;
+    action->fork_cb = fork_cb;
     action->async = 1;
 
-    rc = internal_stonith_action_execute(action);
-
-    return rc < 0 ? rc : action->pid;
+    return internal_stonith_action_execute(action);
 }
 
 /*!
-- 
1.8.3.1