diff --git a/SOURCES/001-crm_resource_wait.patch b/SOURCES/001-crm_resource_wait.patch new file mode 100644 index 0000000..8193769 --- /dev/null +++ b/SOURCES/001-crm_resource_wait.patch @@ -0,0 +1,131 @@ +From 17a2d7acffc86e06b2fa8b49c96c33d72466e7f7 Mon Sep 17 00:00:00 2001 +From: Reid Wahl +Date: Fri, 5 Sep 2025 20:35:31 -0700 +Subject: [PATCH] Fix: tools: Handle large timeouts correctly in crm_resource + --wait + +Previously, if the --timeout value parsed to a value greater than +(UINT_MAX - 999), the wait timeout would overflow. The effective timeout +would be either 0 seconds or 1 second. This is because 999 was added to +the guint value before passing it to pcmk__timeout_ms2s(). + +Now, we simply pass the timeout in milliseconds to +pcmk__timeout_ms2s(), without adding 999. + +This implies a slight behavior change. Previously, timeouts were always +rounded up to the next greatest second. Now, they're rounded to the +nearest second. For example, previously: +* timeout values between 1ms and 500ms => wait timeout of 1 second +* timeout values between 501ms and 1500ms => wait timeout of 2 seconds +* timeout values between 1501ms and 2500ms => wait timeout of 3 seconds +* and so on + +Now: +* timeout values between 1ms and 1499ms => wait timeout of 1 second +* timeout values between 1500ms and 2499ms => wait timeout of 2 seconds +* timeout values between 2500ms and 3499ms => wait timeout of 3 seconds +* and so on + +The previous rounding behavior has existed since crm_resource --wait was +added by 424afcdf. + +Update the help text to note the granularity and rounding behavior. The +exact behavior of the restart command is confusing, and its logic should +be cleaned up in the future. + +Fixes RHEL-45869 +Fixes RHEL-86148 +Closes T841 + +Signed-off-by: Reid Wahl +--- + include/crm/common/internal.h | 1 + + lib/common/utils.c | 30 ++++++++++++++++++++++++++++++ + tools/crm_resource.c | 4 +++- + tools/crm_resource_runtime.c | 2 +- + 4 files changed, 35 insertions(+), 2 deletions(-) + +diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h +index 7d1e95f..ee26321 100644 +--- a/include/crm/common/internal.h ++++ b/include/crm/common/internal.h +@@ -249,6 +249,7 @@ void pcmk__daemonize(const char *name, const char *pidfile); + void pcmk__panic(const char *origin); + pid_t pcmk__locate_sbd(void); + void pcmk__sleep_ms(unsigned int ms); ++guint pcmk__timeout_ms2s(guint timeout_ms); + + extern int pcmk__score_red; + extern int pcmk__score_green; +diff --git a/lib/common/utils.c b/lib/common/utils.c +index 9e44c22..0a38811 100644 +--- a/lib/common/utils.c ++++ b/lib/common/utils.c +@@ -413,6 +413,36 @@ pcmk__sleep_ms(unsigned int ms) + #endif + } + ++/*! ++ * \internal ++ * \brief Convert milliseconds to seconds ++ * ++ * \param[in] timeout_ms The interval, in ms ++ * ++ * \return If \p timeout_ms is 0, return 0. Otherwise, return the number of ++ * seconds, rounded to the nearest integer, with a minimum of 1. ++ */ ++guint ++pcmk__timeout_ms2s(guint timeout_ms) ++{ ++ guint quot, rem; ++ ++ if (timeout_ms == 0) { ++ return 0; ++ } else if (timeout_ms < 1000) { ++ return 1; ++ } ++ ++ quot = timeout_ms / 1000; ++ rem = timeout_ms % 1000; ++ ++ if (rem >= 500) { ++ quot += 1; ++ } ++ ++ return quot; ++} ++ + // Deprecated functions kept only for backward API compatibility + // LCOV_EXCL_START + +diff --git a/tools/crm_resource.c b/tools/crm_resource.c +index 898c21e..ac50c5e 100644 +--- a/tools/crm_resource.c ++++ b/tools/crm_resource.c +@@ -766,7 +766,9 @@ static GOptionEntry addl_entries[] = { + "ID" }, + { "timeout", 'T', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, timeout_cb, + "(Advanced) Abort if command does not finish in this time (with\n" +- INDENT "--restart, --wait, --force-*)", ++ INDENT "--restart, --wait, --force-*). The --restart command uses a\n" ++ INDENT "two-second granularity and the --wait command uses a one-second\n" ++ INDENT "granularity, with rounding.", + "N" }, + { "all", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all, + "List all options, including advanced and deprecated (with\n" +diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c +index 7350f07..286c10c 100644 +--- a/tools/crm_resource_runtime.c ++++ b/tools/crm_resource_runtime.c +@@ -1977,7 +1977,7 @@ wait_till_stable(pcmk__output_t *out, guint timeout_ms, cib_t * cib) + if (timeout_ms == 0) { + expire_time += WAIT_DEFAULT_TIMEOUT_S; + } else { +- expire_time += (timeout_ms + 999) / 1000; ++ expire_time += pcmk__timeout_ms2s(timeout_ms); + } + + scheduler = pe_new_working_set(); +-- +2.47.1 + diff --git a/SOURCES/002-ipc_connect_retry.patch b/SOURCES/002-ipc_connect_retry.patch new file mode 100644 index 0000000..19b8028 --- /dev/null +++ b/SOURCES/002-ipc_connect_retry.patch @@ -0,0 +1,178 @@ +From 8ff58d786907b64c32350e72e341cdd0f5026813 Mon Sep 17 00:00:00 2001 +From: Thomas Jones +Date: Fri, 30 May 2025 16:40:13 -0400 +Subject: [PATCH 1/2] Fix: libcrmcommon: Add retries on connect to avoid fatal + errors when sub-daemons communicate Add pcmk__connect_ipc_retry_conrefused() + and use it where it makes sense Add retry loop to + connect_and_send_attrd_request() that retries connect and send. + +--- + daemons/controld/controld_schedulerd.c | 2 +- + include/crm/common/ipc_internal.h | 4 ++++ + lib/common/ipc_attrd.c | 19 ++++++++++++---- + lib/common/ipc_client.c | 30 ++++++++++++++++++++++++++ + lib/pacemaker/pcmk_cluster_queries.c | 2 +- + 5 files changed, 51 insertions(+), 6 deletions(-) + +diff --git a/daemons/controld/controld_schedulerd.c b/daemons/controld/controld_schedulerd.c +index 22b37b8..444bdef 100644 +--- a/daemons/controld/controld_schedulerd.c ++++ b/daemons/controld/controld_schedulerd.c +@@ -197,7 +197,7 @@ new_schedulerd_ipc_connection(void) + + pcmk_register_ipc_callback(schedulerd_api, scheduler_event_callback, NULL); + +- rc = pcmk__connect_ipc(schedulerd_api, pcmk_ipc_dispatch_main, 3); ++ rc = pcmk__connect_ipc_retry_conrefused(schedulerd_api, pcmk_ipc_dispatch_main, 3); + if (rc != pcmk_rc_ok) { + crm_err("Error connecting to %s: %s", + pcmk_ipc_name(schedulerd_api, true), pcmk_rc_str(rc)); +diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h +index 27b8bfc..09145ba 100644 +--- a/include/crm/common/ipc_internal.h ++++ b/include/crm/common/ipc_internal.h +@@ -101,6 +101,10 @@ int pcmk__ipc_fd(crm_ipc_t *ipc, int *fd); + int pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type, + int attempts); + ++int pcmk__connect_ipc_retry_conrefused(pcmk_ipc_api_t *api, ++ enum pcmk_ipc_dispatch dispatch_type, ++ int attempts); ++ + /* + * Server-related + */ +diff --git a/lib/common/ipc_attrd.c b/lib/common/ipc_attrd.c +index 5ab0f2d..60a92a0 100644 +--- a/lib/common/ipc_attrd.c ++++ b/lib/common/ipc_attrd.c +@@ -152,6 +152,8 @@ create_attrd_op(const char *user_name) + static int + connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request) + { ++ static const int max_retries = 5; ++ int remaining_attempts = max_retries; + int rc = pcmk_rc_ok; + bool created_api = false; + +@@ -163,10 +165,19 @@ connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request) + created_api = true; + } + +- rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5); +- if (rc == pcmk_rc_ok) { +- rc = pcmk__send_ipc_request(api, request); +- } ++ // If attrd is killed and is being restarted we will temporarily get ++ // ECONNREFUSED on connect if it is already dead or ENOTCONN if it died ++ // after we connected to it. We should wait a bit and retry in those cases. ++ do { ++ if (rc == ENOTCONN || rc == ECONNREFUSED) { ++ sleep(max_retries - remaining_attempts); ++ } ++ rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, remaining_attempts); ++ if (rc == pcmk_rc_ok) { ++ rc = pcmk__send_ipc_request(api, request); ++ } ++ remaining_attempts--; ++ } while ((rc == ENOTCONN || rc == ECONNREFUSED) && remaining_attempts >= 0); + + if (created_api) { + pcmk_free_ipc_api(api); +diff --git a/lib/common/ipc_client.c b/lib/common/ipc_client.c +index 24d7745..4ac7810 100644 +--- a/lib/common/ipc_client.c ++++ b/lib/common/ipc_client.c +@@ -488,6 +488,36 @@ connect_without_main_loop(pcmk_ipc_api_t *api) + return rc; + } + ++/*! ++ * \internal ++ * \brief Connect to a Pacemaker daemon via IPC (retrying after soft errors ++ * and ECONNREFUSED) ++ * ++ * \param[in,out] api IPC API instance ++ * \param[in] dispatch_type How IPC replies should be dispatched ++ * \param[in] attempts How many times to try (in case of soft error) ++ * ++ * \return Standard Pacemaker return code ++*/ ++int ++pcmk__connect_ipc_retry_conrefused(pcmk_ipc_api_t *api, ++ enum pcmk_ipc_dispatch dispatch_type, ++ int attempts) ++{ ++ int remaining = attempts; ++ int rc = pcmk_rc_ok; ++ ++ do { ++ if (rc == ECONNREFUSED) { ++ pcmk__sleep_ms((attempts - remaining) * 500); ++ } ++ rc = pcmk__connect_ipc(api, dispatch_type, remaining); ++ remaining--; ++ } while (rc == ECONNREFUSED && remaining >= 0); ++ ++ return rc; ++} ++ + /*! + * \internal + * \brief Connect to a Pacemaker daemon via IPC (retrying after soft errors) +diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c +index bbefcda..c1fcf73 100644 +--- a/lib/pacemaker/pcmk_cluster_queries.c ++++ b/lib/pacemaker/pcmk_cluster_queries.c +@@ -360,7 +360,7 @@ ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb, + pcmk_register_ipc_callback(api, cb, data); + } + +- rc = pcmk__connect_ipc(api, dispatch_type, 5); ++ rc = pcmk__connect_ipc_retry_conrefused(api, dispatch_type, 5); + if (rc != pcmk_rc_ok) { + if (rc == EREMOTEIO) { + data->pcmkd_state = pcmk_pacemakerd_state_remote; +-- +2.47.1 + +From 2e46b914fb08d346d7b022ba75302f9290034507 Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Mon, 4 Aug 2025 10:38:00 -0400 +Subject: [PATCH 2/2] Med: libpacemaker: Do not retry on ECONNREFUSED in tools. + +This is a regression introduced by e438946787. In that patch, what +we're trying to do is retry IPC connections between daemons. If a +daemon gets ECONNREFUSED when it initiates an IPC connection, the most +likely reason is that another daemon has been killed and is restarting +but is not yet ready to accept connections. Waiting and retrying +repeatedly is an acceptable way to deal with this. + +However, if a command line tool gets ECONNREFUSED, it's more likely that +the problem is the cluster isn't running at all. In this case, waiting +and retrying just introduces a delay for a situation that will never be +resolved. Reverting just the part in pcmk_cluster_queries.c should fix +this problem without affecting any of the daemons - they don't call this +code. + +Fixes RHEL-106594 +--- + lib/pacemaker/pcmk_cluster_queries.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c +index c1fcf73..bbefcda 100644 +--- a/lib/pacemaker/pcmk_cluster_queries.c ++++ b/lib/pacemaker/pcmk_cluster_queries.c +@@ -360,7 +360,7 @@ ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb, + pcmk_register_ipc_callback(api, cb, data); + } + +- rc = pcmk__connect_ipc_retry_conrefused(api, dispatch_type, 5); ++ rc = pcmk__connect_ipc(api, dispatch_type, 5); + if (rc != pcmk_rc_ok) { + if (rc == EREMOTEIO) { + data->pcmkd_state = pcmk_pacemakerd_state_remote; +-- +2.47.1 + diff --git a/SOURCES/001-ipc_evict.patch b/SOURCES/003-ipc_evict.patch similarity index 100% rename from SOURCES/001-ipc_evict.patch rename to SOURCES/003-ipc_evict.patch diff --git a/SOURCES/002-fewer_messages.patch b/SOURCES/004-fewer_messages.patch similarity index 100% rename from SOURCES/002-fewer_messages.patch rename to SOURCES/004-fewer_messages.patch diff --git a/SOURCES/005-remote_overflow.patch b/SOURCES/005-remote_overflow.patch new file mode 100644 index 0000000..bc0e588 --- /dev/null +++ b/SOURCES/005-remote_overflow.patch @@ -0,0 +1,413 @@ +From 264d94dcdb4c6739355f7251b4e9e81256938dbf Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Thu, 14 May 2026 12:57:17 -0400 +Subject: [PATCH 1/6] Med: libcrmcommon: Add sanity checks to + localized_remote_header. + +These checks are present in the main and 3.0 branches, and they make +sense here as well. +--- + lib/common/remote.c | 52 ++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 47 insertions(+), 5 deletions(-) + +diff --git a/lib/common/remote.c b/lib/common/remote.c +index b25e5ce..a4c8b8a 100644 +--- a/lib/common/remote.c ++++ b/lib/common/remote.c +@@ -1,5 +1,5 @@ + /* +- * Copyright 2008-2024 the Pacemaker project contributors ++ * Copyright 2008-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -23,7 +23,7 @@ + #include + #include + #include +-#include // PRIx32 ++#include // PRIu32, PRIx32 + + #include + #include +@@ -97,11 +97,18 @@ struct remote_header_v0 { + static struct remote_header_v0 * + localized_remote_header(pcmk__remote_t *remote) + { +- struct remote_header_v0 *header = (struct remote_header_v0 *)remote->buffer; +- if(remote->buffer_offset < sizeof(struct remote_header_v0)) { ++ struct remote_header_v0 *header = NULL; ++ size_t expected_size = 0; ++ ++ if ((remote == NULL) || (remote->buffer == NULL) ++ || (remote->buffer_offset < sizeof(struct remote_header_v0))) { ++ ++ // Caller error or we haven't received the full header yet + return NULL; ++ } + +- } else if(header->endian != ENDIAN_LOCAL) { ++ header = (struct remote_header_v0 *) remote->buffer; ++ if (header->endian != ENDIAN_LOCAL) { + uint32_t endian = __swab32(header->endian); + + CRM_LOG_ASSERT(endian == ENDIAN_LOCAL); +@@ -123,6 +130,41 @@ localized_remote_header(pcmk__remote_t *remote) + header->payload_uncompressed = __swab32(header->payload_uncompressed); + } + ++ // Sanity checks ++ if (header->payload_offset != sizeof(struct remote_header_v0)) { ++ crm_err("Header payload offset %" PRIu32 " does not have expected " ++ "size %zu", header->payload_offset, ++ sizeof(struct remote_header_v0)); ++ return NULL; ++ } ++ ++ if (header->payload_compressed != 0) { ++ if (header->payload_compressed > (SIZE_MAX - header->payload_offset)) { ++ crm_err("Header compressed size %" PRIu32 " is too large", ++ header->payload_compressed); ++ return NULL; ++ } ++ ++ expected_size = (size_t) header->payload_offset ++ + header->payload_compressed; ++ ++ } else { ++ if (header->payload_uncompressed > (SIZE_MAX - header->payload_offset)) { ++ crm_err("Header uncompressed size %" PRIu32 " is too large", ++ header->payload_uncompressed); ++ return NULL; ++ } ++ ++ expected_size = (size_t) header->payload_offset ++ + header->payload_uncompressed; ++ } ++ ++ if (expected_size != header->size_total) { ++ crm_err("Header total size %" PRIu32 " does not match calculated " ++ "size %zu", header->size_total, expected_size); ++ return NULL; ++ } ++ + return header; + } + +-- +2.53.0 + +From 8d21d297629bcc8aebf71bb171c550bcc4532c3f Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Thu, 14 May 2026 13:06:57 -0400 +Subject: [PATCH 2/6] High: libcrmcommon: Fix integer overflow in remote + message code. + +It's possible for a client to send a specifically constructed header +packet that will cause our size checks in pcmk__remote_message_xml to +overflow, which could result in crashes (potentially leading to a node +being fenced) or memory corruption problems. + +Such a header would have to indicate that the payload is compressed, +which nothing in pacemaker is doing. In fact, we haven't used any of +the remote message compression code since it was introduced in 2013 +which explains why we haven't seen any problems with it. Thus, it would +require a malicious client to cause this to happen. + +It would also require remote CIB administration to be enabled via +remote-clear-port or remote-tls-port, which we do not do by default. +Additionally, if remote-tls-port is in use, it requires the client to +complete the gnutls handshake procedure (pcmk__remote_message_xml is +only called in cib_remote_msg after the handshake but before the client +has authenticated). + +Co-Authored-By: Reid Wahl +--- + lib/common/remote.c | 49 ++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 42 insertions(+), 7 deletions(-) + +diff --git a/lib/common/remote.c b/lib/common/remote.c +index a4c8b8a..aa8e762 100644 +--- a/lib/common/remote.c ++++ b/lib/common/remote.c +@@ -619,16 +619,51 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) + } + + /* Support compression on the receiving end now, in case we ever want to add it later */ +- if (header->payload_compressed) { ++ if (header->payload_compressed != 0) { + int rc = 0; +- unsigned int size_u = 1 + header->payload_uncompressed; +- char *uncompressed = +- pcmk__assert_alloc(1, header->payload_offset + size_u); ++ unsigned int size_u = 0; ++ char *uncompressed = NULL; ++ ++#if (UINT32_MAX < UINT_MAX) ++ if (header->payload_uncompressed >= UINT_MAX) { ++ crm_err("Couldn't decompress message because uncompressed " ++ "payload size (%" PRIu32 ") is greater than UINT_MAX " ++ "(%u)", header->payload_uncompressed, UINT_MAX); ++ return NULL; ++ } ++#endif ++ ++ /* @TODO Is the extra byte for the null terminator? ++ * pcmk__remote_send_xml() also adds one byte to the iov length. ++ * (However, we do need to account for the possibility of receiving a ++ * message from an untrusted sender.) ++ */ ++ size_u = 1 + header->payload_uncompressed; ++ ++ /* Header and uncompressed payload must fit in the destination buffer. ++ * We do not need to separately check the header size here since ++ * localized_remote_header will return NULL if it's incorrect. ++ */ ++#if (UINT_MAX >= SIZE_MAX) ++ if ((size_u >= SIZE_MAX) ++ || (header->payload_offset > (SIZE_MAX - size_u))) { ++#else ++ if (header->payload_offset > (SIZE_MAX - size_u)) { ++#endif ++ crm_err("Couldn't decompress message because the required buffer " ++ "size (%" PRIu32 " + %u) is greater than SIZE_MAX (%zu)", ++ header->payload_offset, size_u, SIZE_MAX); ++ return NULL; ++ } ++ ++ crm_trace("Decompressing message data %" PRIu32 " bytes into %u " ++ "bytes", header->payload_compressed, size_u); + +- crm_trace("Decompressing message data %d bytes into %d bytes", +- header->payload_compressed, size_u); ++ uncompressed = pcmk__assert_alloc(header->payload_offset + size_u, ++ sizeof(char)); + +- rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, &size_u, ++ rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, ++ &size_u, + remote->buffer + header->payload_offset, + header->payload_compressed, 1, 0); + rc = pcmk__bzlib2rc(rc); +-- +2.53.0 + +From 77a530d1bb867264fa0d42752da44d75ee063830 Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Thu, 14 May 2026 13:26:07 -0400 +Subject: [PATCH 3/6] High: libcrmcommon: Limit the max size of a remote + message. + +Previously, we were just taking the sizes straight from the message +header and allocating however much memory was asked for without +checking. It's possible for a malicious client to ask for a very large +amount of memory (say, 4 GB since that'll fit in the uint32_t value for +header->size_total). + +In that case, due to the use of pcmk__assert_alloc, we'll crash due to +an out of memory error and the node could potentially be fenced. + +The fix is to simply check that the client isn't asking for too much +memory. This does unfortunately impose a size restriction on the +message. It's doubly unfortunate given how much work we've recently put +into removing restrictions on IPC messages allowing for much larger +clusters. + +I think the biggest thing these messages will ever contain is a CIB, so +for now I'm going with a limit of 20 MB. It is not my intention that +this become a tuneable parameter that is exposed to the user. +--- + include/crm/common/remote_internal.h | 5 ++++- + lib/common/remote.c | 17 +++++++++++++++-- + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/include/crm/common/remote_internal.h b/include/crm/common/remote_internal.h +index f8c0bea..00f5918 100644 +--- a/include/crm/common/remote_internal.h ++++ b/include/crm/common/remote_internal.h +@@ -1,5 +1,5 @@ + /* +- * Copyright 2008-2024 the Pacemaker project contributors ++ * Copyright 2008-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -15,6 +15,9 @@ + #include // pcmk_node_variant_remote + #include // pcmk_node_t + ++// The maximum payload size for a remote message (in bytes) ++#define PCMK__REMOTE_MSG_MAX_SIZE (20 * 1024 * 1024) ++ + // internal functions from remote.c + + typedef struct pcmk__remote_s pcmk__remote_t; +diff --git a/lib/common/remote.c b/lib/common/remote.c +index aa8e762..08b6988 100644 +--- a/lib/common/remote.c ++++ b/lib/common/remote.c +@@ -623,6 +623,7 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) + int rc = 0; + unsigned int size_u = 0; + char *uncompressed = NULL; ++ size_t buffer_size = 0; + + #if (UINT32_MAX < UINT_MAX) + if (header->payload_uncompressed >= UINT_MAX) { +@@ -656,11 +657,17 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) + return NULL; + } + ++ buffer_size = (size_t) header->payload_offset + size_u; ++ if (buffer_size > PCMK__REMOTE_MSG_MAX_SIZE) { ++ crm_err("Message size %zu is larger than max allowed %u bytes", ++ buffer_size, PCMK__REMOTE_MSG_MAX_SIZE); ++ return NULL; ++ } ++ + crm_trace("Decompressing message data %" PRIu32 " bytes into %u " + "bytes", header->payload_compressed, size_u); + +- uncompressed = pcmk__assert_alloc(header->payload_offset + size_u, +- sizeof(char)); ++ uncompressed = pcmk__assert_alloc(buffer_size, sizeof(char)); + + rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, + &size_u, +@@ -805,6 +812,12 @@ pcmk__read_available_remote_data(pcmk__remote_t *remote) + read_len = header->size_total; + } + ++ if (read_len > PCMK__REMOTE_MSG_MAX_SIZE) { ++ crm_err("Message size %zu is larger than max allowed %u bytes", ++ read_len, PCMK__REMOTE_MSG_MAX_SIZE); ++ return EINVAL; ++ } ++ + /* automatically grow the buffer when needed */ + if(remote->buffer_size < read_len) { + remote->buffer_size = 2 * read_len; +-- +2.53.0 + +From eaae2d00a2d29c0187832a55a81cf42b39d93a25 Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Fri, 15 May 2026 10:19:33 -0400 +Subject: [PATCH 4/6] High: libcrmcommon: Fix an integer overflow in + pcmk__remote_send_xml. + +iov[0].iov_len is a size_t. iov[1].iov_len is also a size_t (which +comes from the length of a GString, which is a gsize, which is also a +size_t). We're adding those together and storing them in a uint32_t, so +make sure the result will fit. + +Fixes RHEL-181155 +--- + lib/common/remote.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/lib/common/remote.c b/lib/common/remote.c +index 08b6988..83f050d 100644 +--- a/lib/common/remote.c ++++ b/lib/common/remote.c +@@ -586,6 +586,13 @@ pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg) + header->version = REMOTE_MSG_VERSION; + header->payload_offset = iov[0].iov_len; + header->payload_uncompressed = iov[1].iov_len; ++ ++ if ((UINT32_MAX - iov[0].iov_len) < iov[1].iov_len) { ++ crm_err("Remote message size %zu + %zu exceeds maximum of %" PRIu32, ++ iov[0].iov_len, iov[1].iov_len, UINT32_MAX); ++ goto done; ++ } ++ + header->size_total = iov[0].iov_len + iov[1].iov_len; + + rc = remote_send_iovs(remote, iov, 2); +@@ -594,6 +601,7 @@ pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg) + pcmk_rc_str(rc), rc); + } + ++done: + free(iov[0].iov_base); + g_free((gchar *) iov[1].iov_base); + return rc; +-- +2.53.0 + +From 2e097d03f1d93f95162a37a5c21ec46d911af176 Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Thu, 28 May 2026 12:56:57 -0400 +Subject: [PATCH 5/6] Refactor: libcib: Remove an unnecessary coverity + suppression. + +With all the previous changes, this suppression is no longer necessary +and can be removed. +--- + lib/cib/cib_remote.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c +index baed17b..a64ff95 100644 +--- a/lib/cib/cib_remote.c ++++ b/lib/cib/cib_remote.c +@@ -243,7 +243,6 @@ cib_remote_callback_dispatch(gpointer user_data) + return -1; + } + +- // coverity[tainted_data] This can't easily be changed right now + msg = pcmk__remote_message_xml(&private->callback); + if (msg == NULL) { + private->start_time = 0; +-- +2.53.0 + +From 2a3c713c3ceab8f5aded9693d09203d18b33bf55 Mon Sep 17 00:00:00 2001 +From: Chris Lumens +Date: Wed, 17 Jun 2026 15:55:28 -0400 +Subject: [PATCH 6/6] Med: libcrmcommon: Add additional checks to + pcmk__remote_message_xml. + +coverity flags the existing code for using a tainted expression as an +index to a pointer. While we're fixing other range errors and overflows +in this code, we should fix this one as well by porting the existing +code from the main branch. +--- + lib/common/remote.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/lib/common/remote.c b/lib/common/remote.c +index 83f050d..9ad4b17 100644 +--- a/lib/common/remote.c ++++ b/lib/common/remote.c +@@ -620,6 +620,7 @@ xmlNode * + pcmk__remote_message_xml(pcmk__remote_t *remote) + { + xmlNode *xml = NULL; ++ size_t data_size = 0; + struct remote_header_v0 *header = localized_remote_header(remote); + + if (header == NULL) { +@@ -709,7 +710,18 @@ pcmk__remote_message_xml(pcmk__remote_t *remote) + /* take ownership of the buffer */ + remote->buffer_offset = 0; + +- CRM_LOG_ASSERT(remote->buffer[sizeof(struct remote_header_v0) + header->payload_uncompressed - 1] == 0); ++ data_size = (size_t) header->payload_offset + header->payload_uncompressed; ++ ++ // Ensure the buffer is as big as it should be ++ CRM_CHECK(remote->buffer_size >= data_size, return NULL); ++ ++ /* Ensure the buffer is null-terminated (see ++ * pcmk__read_available_remote_data()). ++ * ++ * Note that payload_uncompressed contains the payload size including the ++ * null byte (see pcmk__remote_send_xml()). ++ */ ++ CRM_CHECK(remote->buffer[data_size] == '\0', return NULL); + + xml = pcmk__xml_parse(remote->buffer + header->payload_offset); + if (xml == NULL && header->version > REMOTE_MSG_VERSION) { +-- +2.53.0 + diff --git a/SPECS/pacemaker.spec b/SPECS/pacemaker.spec index 2b28292..ce2c715 100644 --- a/SPECS/pacemaker.spec +++ b/SPECS/pacemaker.spec @@ -22,7 +22,7 @@ ## Where bug reports should be submitted ## Leave bug_url undefined to use ClusterLabs default, others define it here %if 0%{?rhel} -%global bug_url https://issues.redhat.com/ +%global bug_url https://github.com/oracle/oracle-linux/ %else %if 0%{?fedora} %global bug_url https://bugz.fedoraproject.org/%{name} @@ -36,7 +36,7 @@ ## can be incremented to build packages reliably considered "newer" ## than previously built packages with the same pcmkversion) %global pcmkversion 2.1.10 -%global specversion 1 +%global specversion 3 ## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build %global commit 5693eaeeef06faa1622515963082b5a1731d9fc0 @@ -230,7 +230,7 @@ Name: pacemaker Summary: Scalable High-Availability cluster resource manager Version: %{pcmkversion} -Release: %{pcmk_release}.1%{?dist} +Release: %{pcmk_release}.0.1%{?dist} License: GPL-2.0-or-later AND LGPL-2.1-or-later Url: https://www.clusterlabs.org/ @@ -247,8 +247,11 @@ Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz Source2: pacemaker.sysusers # upstream commits -Patch001: 001-ipc_evict.patch -Patch002: 002-fewer_messages.patch +Patch001: 001-crm_resource_wait.patch +Patch002: 002-ipc_connect_retry.patch +Patch003: 003-ipc_evict.patch +Patch004: 004-fewer_messages.patch +Patch005: 005-remote_overflow.patch Requires: resource-agents Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release} @@ -915,10 +918,22 @@ exit 0 %license %{nagios_name}-%{nagios_hash}/COPYING %changelog -* Wed Feb 18 2026 Chris Lumens - 2.1.10-1.1 +* Tue Jul 14 2026 EL Errata - 2.1.10-3.0.1 +- Replace bug url [Orabug: 34202300] +- Upstream reference in pacemaker crm_report binary [Orabug: 32825154] + +* Tue Jun 30 2026 Chris Lumens - 2.1.10-3 +- Fix integer overflows in remote message decompression code (CVE-2026-10649) +- Resolves: RHEL-181150 + +* Wed Nov 12 2025 Chris Lumens - 2.1.10-2 +- Handle large timeouts correctly in crm_resource --wait +- Do not try to connect to subdaemons before they're respawned - Don't evict IPC clients as long as they're still processing messages - Don't overwhelm the FSA queue with repeated CIB queries -- Resolves: RHEL-150167 +- Resolves: RHEL-45869 +- Resolves: RHEL-87484 +- Resolves: RHEL-114894 * Mon Jun 23 2025 Chris Lumens - 2.1.10-1 - Rebase on upstream 2.1.10-rc1 release