From dd521e724c4c2d4f074ffcf30a9e7a844fcfb7d2 Mon Sep 17 00:00:00 2001 From: Klaus Wenninger 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