From ab558544b83267b7f1825627f4bed963227d3e05 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 28 2017 15:11:56 +0000 Subject: import pacemaker-1.1.15-11.el7_3.5 --- diff --git a/SOURCES/021-static-tables.patch b/SOURCES/021-static-tables.patch new file mode 100644 index 0000000..6d64bc8 --- /dev/null +++ b/SOURCES/021-static-tables.patch @@ -0,0 +1,105 @@ +From 6b12e0d1284cac96e66780aa4aeb7c32619089fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 9 Feb 2017 22:46:42 +0100 +Subject: [PATCH 1/2] Refactor: lib/services: ensure *.h declare externally + hidden functions + +...following the precedent of {upstart,systemd}.h. Note also that +since hardening-related commit 658fff944 [$LD -z now], it's even more +desirable to limit "symbol smog" as much as possible and symbols only +used within the library like these are trivially expendable (assuming +suitable toolchain). + +As we use G_GNU_C_INTERNAL macro from glib2, we should care to require +at least that version that introduced it, i.e., 2.6 per documentation. +--- + lib/services/services_private.h | 14 ++++++++++++++ + 1 file changed, 13 insertions(+), 0 deletions(-) + +diff --git a/lib/services/services_private.h b/lib/services/services_private.h +index 60b1b14..41895a9 100644 +--- a/lib/services/services_private.h ++++ b/lib/services/services_private.h +@@ -45,30 +45,44 @@ struct svc_action_private_s { + #endif + }; + ++G_GNUC_INTERNAL + GList *services_os_get_directory_list(const char *root, gboolean files, gboolean executable); + ++G_GNUC_INTERNAL + gboolean services_os_action_execute(svc_action_t * op, gboolean synchronous); + ++G_GNUC_INTERNAL + GList *resources_os_list_lsb_agents(void); + ++G_GNUC_INTERNAL + GList *resources_os_list_ocf_providers(void); + ++G_GNUC_INTERNAL + GList *resources_os_list_ocf_agents(const char *provider); + ++G_GNUC_INTERNAL + GList *resources_os_list_nagios_agents(void); + ++G_GNUC_INTERNAL + gboolean cancel_recurring_action(svc_action_t * op); + ++G_GNUC_INTERNAL + gboolean recurring_action_timer(gpointer data); ++ ++G_GNUC_INTERNAL + gboolean operation_finalize(svc_action_t * op); + ++G_GNUC_INTERNAL + void services_add_inflight_op(svc_action_t *op); + ++G_GNUC_INTERNAL + void handle_blocked_ops(void); + ++G_GNUC_INTERNAL + gboolean is_op_blocked(const char *rsc); + + #if SUPPORT_DBUS ++G_GNUC_INTERNAL + void services_set_op_pending(svc_action_t *op, DBusPendingCall *pending); + #endif + +-- +1.8.3.1 + + +From 31adceb587c1beb90a0d61bb3bf1b634e5516d54 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Fri, 10 Feb 2017 10:50:59 +0100 +Subject: [PATCH 2/2] Refactor: lib/services: ensure some objects not exported + accidentally + +They are not tracked by neither public nor private API anyway. +See also a note about "symbol smog" in the previous commit. +--- + lib/services/services.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 4be425c..b1e3b26 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -37,11 +37,11 @@ + /* TODO: Develop a rollover strategy */ + + static int operations = 0; +-GHashTable *recurring_actions = NULL; ++static GHashTable *recurring_actions = NULL; + + /* ops waiting to run async because of conflicting active +- * pending ops*/ +-GList *blocked_ops = NULL; ++ * pending ops */ ++static GList *blocked_ops = NULL; + + /* ops currently active (in-flight) */ + GList *inflight_ops = NULL; +-- +1.8.3.1 + diff --git a/SOURCES/022-inflight-systemd.patch b/SOURCES/022-inflight-systemd.patch new file mode 100644 index 0000000..68b6c89 --- /dev/null +++ b/SOURCES/022-inflight-systemd.patch @@ -0,0 +1,466 @@ +From 47f957c747f76d54b7289d9651aa057efb5be4c7 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 27 Feb 2017 14:10:02 -0600 +Subject: [PATCH 1/7] Fix: libcrmcommon: assert if can't generate operation key + +This is a refactor of generate_op_key() to use the existing +crm_strdup_printf() function instead of its own allocation, +and use CRM_ASSERT() instead of CRM_CHECK() for argument validation. + +This simplifies the code, avoids allocating more memory than needed, +and results in assertion failures instead of returning NULL on errors, +which is preferable because existing callers don't check the +return value for NULL before using it. +--- + lib/common/utils.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/lib/common/utils.c b/lib/common/utils.c +index 3e3abd3..dbf84c0 100644 +--- a/lib/common/utils.c ++++ b/lib/common/utils.c +@@ -629,21 +629,24 @@ crm_get_msec(const char *input) + return msec; + } + ++/*! ++ * \brief Generate an operation key ++ * ++ * \param[in] rsc_id ID of resource being operated on ++ * \param[in] op_type Operation name ++ * \param[in] interval Operation interval ++ * ++ * \return Newly allocated memory containing operation key as string ++ * ++ * \note It is the caller's responsibility to free() the result. ++ */ + char * + generate_op_key(const char *rsc_id, const char *op_type, int interval) + { +- int len = 35; +- char *op_id = NULL; +- +- CRM_CHECK(rsc_id != NULL, return NULL); +- CRM_CHECK(op_type != NULL, return NULL); +- +- len += strlen(op_type); +- len += strlen(rsc_id); +- op_id = malloc(len); +- CRM_CHECK(op_id != NULL, return NULL); +- sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval); +- return op_id; ++ CRM_ASSERT(rsc_id != NULL); ++ CRM_ASSERT(op_type != NULL); ++ CRM_ASSERT(interval >= 0); ++ return crm_strdup_printf("%s_%s_%d", rsc_id, op_type, interval); + } + + gboolean +-- +1.8.3.1 + + +From 94a4c58f675d163085a055f59fd6c3a2c9f57c43 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 27 Feb 2017 14:17:01 -0600 +Subject: [PATCH 2/7] Fix: libservices: ensure completed ops aren't on blocked + ops list + +This refactors removal of a completed operation from the in-flight ops list +into a new internal function services_untrack_op(), which allows the +in-flight ops list and handle_blocked_ops() to be static to services.c. + +The new function also ensures the op isn't on the blocked ops list, +which could otherwise result in a use-after-free bug. +--- + lib/services/services.c | 23 +++++++++++++++++++++-- + lib/services/services_linux.c | 7 +------ + lib/services/services_private.h | 2 +- + 3 files changed, 23 insertions(+), 9 deletions(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 0b535e6..90a2181 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -44,7 +44,9 @@ static GHashTable *recurring_actions = NULL; + static GList *blocked_ops = NULL; + + /* ops currently active (in-flight) */ +-GList *inflight_ops = NULL; ++static GList *inflight_ops = NULL; ++ ++static void handle_blocked_ops(void); + + svc_action_t * + services_action_create(const char *name, const char *action, int interval, int timeout) +@@ -605,6 +607,23 @@ services_add_inflight_op(svc_action_t * op) + } + } + ++/*! ++ * \internal ++ * \brief Stop tracking an operation that completed ++ * ++ * \param[in] op Operation to stop tracking ++ */ ++void ++services_untrack_op(svc_action_t *op) ++{ ++ /* Op is no longer in-flight or blocked */ ++ inflight_ops = g_list_remove(inflight_ops, op); ++ blocked_ops = g_list_remove(blocked_ops, op); ++ ++ /* Op is no longer blocking other ops, so check if any need to run */ ++ handle_blocked_ops(); ++} ++ + gboolean + services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t *)) + { +@@ -649,7 +668,7 @@ is_op_blocked(const char *rsc) + return FALSE; + } + +-void ++static void + handle_blocked_ops(void) + { + GList *executed_ops = NULL; +diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c +index adfefaa..1fe3ec1 100644 +--- a/lib/services/services_linux.c ++++ b/lib/services/services_linux.c +@@ -36,9 +36,6 @@ + # include "crm/common/cib_secrets.h" + #endif + +-/* ops currently active (in-flight) */ +-extern GList *inflight_ops; +- + static inline void + set_fd_opts(int fd, int opts) + { +@@ -248,9 +245,7 @@ operation_finalize(svc_action_t * op) + + op->pid = 0; + +- inflight_ops = g_list_remove(inflight_ops, op); +- +- handle_blocked_ops(); ++ services_untrack_op(op); + + if (!recurring && op->synchronous == FALSE) { + /* +diff --git a/lib/services/services_private.h b/lib/services/services_private.h +index ec9e1c4..a949592 100644 +--- a/lib/services/services_private.h ++++ b/lib/services/services_private.h +@@ -76,7 +76,7 @@ G_GNUC_INTERNAL + void services_add_inflight_op(svc_action_t *op); + + G_GNUC_INTERNAL +-void handle_blocked_ops(void); ++void services_untrack_op(svc_action_t *op); + + G_GNUC_INTERNAL + gboolean is_op_blocked(const char *rsc); +-- +1.8.3.1 + + +From b938c47740a456d0057438e4f5e6029d0e084587 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 27 Feb 2017 14:20:05 -0600 +Subject: [PATCH 3/7] Refactor: libservices: dynamically allocate operation key + +reduces code duplication, and avoids memory over-allocation +--- + lib/services/services.c | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 90a2181..4020b7d 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -149,10 +149,7 @@ resources_action_create(const char *name, const char *standard, const char *prov + op->agent = strdup(agent); + op->sequence = ++operations; + op->flags = flags; +- +- if (asprintf(&op->id, "%s_%s_%d", name, action, interval) == -1) { +- goto return_error; +- } ++ op->id = generate_op_key(name, action, interval); + + if (strcasecmp(op->standard, "service") == 0) { + const char *expanded = resources_find_service_class(op->agent); +@@ -467,11 +464,10 @@ gboolean + services_action_cancel(const char *name, const char *action, int interval) + { + svc_action_t *op = NULL; +- char id[512]; +- +- snprintf(id, sizeof(id), "%s_%s_%d", name, action, interval); ++ char *id = generate_op_key(name, action, interval); + + if (!(op = g_hash_table_lookup(recurring_actions, id))) { ++ free(id); + return FALSE; + } + +@@ -494,10 +490,12 @@ services_action_cancel(const char *name, const char *action, int interval) + /* even though the early termination failed, + * the op will be marked as cancelled once it completes. */ + crm_err("Termination of %s (pid=%d) failed", id, op->pid); ++ free(id); + return FALSE; + } + } + ++ free(id); + return TRUE; + } + +@@ -505,11 +503,7 @@ gboolean + services_action_kick(const char *name, const char *action, int interval /* ms */) + { + svc_action_t * op = NULL; +- char *id = NULL; +- +- if (asprintf(&id, "%s_%s_%d", name, action, interval) == -1) { +- return FALSE; +- } ++ char *id = generate_op_key(name, action, interval); + + op = g_hash_table_lookup(recurring_actions, id); + free(id); +-- +1.8.3.1 + + +From 933d46ef20591757301784773a37e06b78906584 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 27 Feb 2017 14:28:36 -0600 +Subject: [PATCH 4/7] Fix: libservices: prevent use-after-free when freeing an + operation + +--- + lib/services/services.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 4020b7d..78a0781 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -410,6 +410,16 @@ services_action_free(svc_action_t * op) + return; + } + ++ /* The operation should be removed from all tracking lists by this point. ++ * If it's not, we have a bug somewhere, so bail. That may lead to a ++ * memory leak, but it's better than a use-after-free segmentation fault. ++ */ ++ CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return); ++ CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return); ++ CRM_CHECK((recurring_actions == NULL) ++ || (g_hash_table_lookup(recurring_actions, op->id) == NULL), ++ return); ++ + services_action_cleanup(op); + + if (op->opaque->repeat_timer) { +-- +1.8.3.1 + + +From dc36d4375c049024a6f9e4d2277a3e6444fad05b Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 28 Feb 2017 15:41:10 -0600 +Subject: [PATCH 5/7] Refactor: libservices: handle in-flight case first when + cancelling an operation + +will make next bugfix easier +--- + lib/services/services.c | 65 ++++++++++++++++++++++++++++++++----------------- + 1 file changed, 42 insertions(+), 23 deletions(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 78a0781..dba4e66 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -470,43 +470,62 @@ cancel_recurring_action(svc_action_t * op) + return TRUE; + } + ++/*! ++ * \brief Cancel a recurring action ++ * ++ * \param[in] name Name of resource that operation is for ++ * \param[in] action Name of operation to cancel ++ * \param[in] interval Interval of operation to cancel ++ * ++ * \return TRUE if action was successfully cancelled, FALSE otherwise ++ */ + gboolean + services_action_cancel(const char *name, const char *action, int interval) + { +- svc_action_t *op = NULL; ++ gboolean cancelled = FALSE; + char *id = generate_op_key(name, action, interval); ++ svc_action_t *op = g_hash_table_lookup(recurring_actions, id); + +- if (!(op = g_hash_table_lookup(recurring_actions, id))) { +- free(id); +- return FALSE; ++ /* We can only cancel a recurring action */ ++ if (op == NULL) { ++ goto done; + } + +- /* Always kill the recurring timer */ ++ /* Tell operation_finalize() not to reschedule the operation */ ++ op->cancel = TRUE; ++ ++ /* Stop tracking it as a recurring operation, and stop its timer */ + cancel_recurring_action(op); + +- if (op->pid == 0) { +- op->status = PCMK_LRM_OP_CANCELLED; +- if (op->opaque->callback) { +- op->opaque->callback(op); ++ /* If the op has a PID, it's an in-flight child process, so kill it. ++ * ++ * Whether the kill succeeds or fails, the main loop will send the op to ++ * operation_finished() (and thus operation_finalize()) when the process ++ * goes away. ++ */ ++ if (op->pid != 0) { ++ crm_info("Terminating in-flight op %s (pid %d) early because it was cancelled", ++ id, op->pid); ++ cancelled = mainloop_child_kill(op->pid); ++ if (cancelled == FALSE) { ++ crm_err("Termination of %s (pid %d) failed", id, op->pid); + } ++ goto done; ++ } + +- blocked_ops = g_list_remove(blocked_ops, op); +- services_action_free(op); +- +- } else { +- crm_info("Cancelling in-flight op: performing early termination of %s (pid=%d)", id, op->pid); +- op->cancel = 1; +- if (mainloop_child_kill(op->pid) == FALSE) { +- /* even though the early termination failed, +- * the op will be marked as cancelled once it completes. */ +- crm_err("Termination of %s (pid=%d) failed", id, op->pid); +- free(id); +- return FALSE; +- } ++ /* Otherwise, operation is not in-flight, just report as cancelled */ ++ op->status = PCMK_LRM_OP_CANCELLED; ++ if (op->opaque->callback) { ++ op->opaque->callback(op); + } + ++ blocked_ops = g_list_remove(blocked_ops, op); ++ services_action_free(op); ++ cancelled = TRUE; ++ ++done: + free(id); +- return TRUE; ++ return cancelled; + } + + gboolean +-- +1.8.3.1 + + +From deabcc5a6aa93dadf0b20364715b559a5b9848ac Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 28 Feb 2017 15:48:11 -0600 +Subject: [PATCH 6/7] Fix: libservices: properly cancel in-flight + systemd/upstart op + +--- + lib/services/services.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/lib/services/services.c b/lib/services/services.c +index dba4e66..4aa4c01 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -89,6 +89,21 @@ resources_find_service_class(const char *agent) + return NULL; + } + ++/*! ++ * \internal ++ * \brief Check whether op is in-flight systemd or upstart op ++ * ++ * \param[in] op Operation to check ++ * ++ * \return TRUE if op is in-flight systemd or upstart op ++ */ ++static inline gboolean ++inflight_systemd_or_upstart(svc_action_t *op) ++{ ++ return (safe_str_eq(op->standard, "systemd") ++ || safe_str_eq(op->standard, "upstart")) ++ && (g_list_find(inflight_ops, op) != NULL); ++} + + svc_action_t * + resources_action_create(const char *name, const char *standard, const char *provider, +@@ -513,6 +528,18 @@ services_action_cancel(const char *name, const char *action, int interval) + goto done; + } + ++ /* In-flight systemd and upstart ops don't have a pid. The relevant handlers ++ * will call operation_finalize() when the operation completes. ++ * @TODO: Can we request early termination, maybe using ++ * dbus_pending_call_cancel()? ++ */ ++ if (inflight_systemd_or_upstart(op)) { ++ crm_info("Will cancel %s op %s when in-flight instance completes", ++ op->standard, op->id); ++ cancelled = FALSE; ++ goto done; ++ } ++ + /* Otherwise, operation is not in-flight, just report as cancelled */ + op->status = PCMK_LRM_OP_CANCELLED; + if (op->opaque->callback) { +-- +1.8.3.1 + + +From b85037b75255061a41d0ec3fd9b64f271351b43e Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 28 Feb 2017 16:43:52 -0600 +Subject: [PATCH 7/7] Fix: libservices: properly detect in-flight + systemd/upstart ops when kicking + +--- + lib/services/services.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 4aa4c01..b41517d 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -568,7 +568,8 @@ services_action_kick(const char *name, const char *action, int interval /* ms */ + return FALSE; + } + +- if (op->pid) { ++ ++ if (op->pid || inflight_systemd_or_upstart(op)) { + return TRUE; + } else { + if (op->opaque->repeat_timer) { +-- +1.8.3.1 + diff --git a/SOURCES/023-recurring-null.patch b/SOURCES/023-recurring-null.patch new file mode 100644 index 0000000..b8d9c65 --- /dev/null +++ b/SOURCES/023-recurring-null.patch @@ -0,0 +1,74 @@ +From 1c534b5a773df5b62aeb8a46842d1e2d4d2266ef Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Wed, 1 Mar 2017 15:04:35 -0600 +Subject: [PATCH] Fix: libservices: ensure recurring actions table is created + before using + +--- + lib/services/services.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/lib/services/services.c b/lib/services/services.c +index 52d3b55..679f8e7 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -90,6 +90,15 @@ resources_find_service_class(const char *agent) + return NULL; + } + ++static inline void ++init_recurring_actions(void) ++{ ++ if (recurring_actions == NULL) { ++ recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, ++ NULL); ++ } ++} ++ + /*! + * \internal + * \brief Check whether op is in-flight systemd or upstart op +@@ -513,9 +522,11 @@ services_action_cancel(const char *name, const char *action, int interval) + { + gboolean cancelled = FALSE; + char *id = generate_op_key(name, action, interval); +- svc_action_t *op = g_hash_table_lookup(recurring_actions, id); ++ svc_action_t *op = NULL; + + /* We can only cancel a recurring action */ ++ init_recurring_actions(); ++ op = g_hash_table_lookup(recurring_actions, id); + if (op == NULL) { + goto done; + } +@@ -575,6 +586,7 @@ services_action_kick(const char *name, const char *action, int interval /* ms */ + svc_action_t * op = NULL; + char *id = generate_op_key(name, action, interval); + ++ init_recurring_actions(); + op = g_hash_table_lookup(recurring_actions, id); + free(id); + +@@ -604,11 +616,6 @@ handle_duplicate_recurring(svc_action_t * op, void (*action_callback) (svc_actio + { + svc_action_t * dup = NULL; + +- if (recurring_actions == NULL) { +- recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); +- return FALSE; +- } +- + /* check for duplicates */ + dup = g_hash_table_lookup(recurring_actions, op->id); + +@@ -700,6 +707,7 @@ services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t * + } + + if (op->interval > 0) { ++ init_recurring_actions(); + if (handle_duplicate_recurring(op, action_callback) == TRUE) { + /* entry rescheduled, dup freed */ + /* exit early */ +-- +1.8.3.1 + diff --git a/SPECS/pacemaker.spec b/SPECS/pacemaker.spec index 055a370..64afefb 100644 --- a/SPECS/pacemaker.spec +++ b/SPECS/pacemaker.spec @@ -83,7 +83,7 @@ Name: pacemaker Summary: Scalable High-Availability cluster resource manager Version: %{pcmkversion} -Release: %{pcmk_release}%{?dist}.4 +Release: %{pcmk_release}%{?dist}.5 %if %{defined _unitdir} License: GPLv2+ and LGPLv2+ %else @@ -118,6 +118,9 @@ Patch17: 017-guest-fencing-tests.patch Patch18: 018-guest-fencing-cl5247-test.patch Patch19: 019-guest-fencing-status.patch Patch20: 020-crm_node-remote.patch +Patch21: 021-static-tables.patch +Patch22: 022-inflight-systemd.patch +Patch23: 023-recurring-null.patch # patches that aren't from upstream Patch100: CVE-2016-7035.patch @@ -742,6 +745,10 @@ exit 0 %attr(0644,root,root) %{_datadir}/pacemaker/nagios/plugins-metadata/* %changelog +* Tue May 16 2017 Ken Gaillot - 1.1.15-11.5 +- Avoid lrmd segmentation fault when cancelling in-flight systemd operation +- Resolves: rhbz#1452196 + * Wed Feb 8 2017 Ken Gaillot - 1.1.15-11.4 - Allow resource agents to correctly determine remote node name - Resolves: rhbz#1420426