VYPR
High severity8.6NVD Advisory· Published Jun 16, 2026· Updated Jun 16, 2026

CVE-2026-10649

CVE-2026-10649

Description

Integer overflow in Pacemaker's remote message decompression allows unauthenticated remote attackers to cause denial of service via crafted compressed messages.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Integer overflow in Pacemaker's remote message decompression allows unauthenticated remote attackers to cause denial of service via crafted compressed messages.

Vulnerability

An integer overflow vulnerability exists in the pcmk__remote_message_xml() function in lib/common/remote.c of Pacemaker. This affects pacemaker-3.0.1-5.el10 when the CIB remote listener is enabled via the remote-clear-port or remote-tls-port configuration options (not the default) [1][2]. The vulnerable code path handles compressed remote messages, a feature unused by any legitimate Pacemaker component since its introduction in 2013 [4]. By sending a crafted remote message header that indicates a compressed payload with manipulated size fields, an attacker can trigger an integer overflow in the size_total check, leading to memory corruption [2][4].

Exploitation

An unauthenticated remote attacker with network access to the CIB remote listener can exploit this vulnerability. For remote-tls-port, a TLS handshake must be completed first, but no authentication is required [3]. The attacker sends a specially crafted remote message header that sets the compressed flag and provides attacker-chosen payload size values. During pcmk__remote_message_xml(), the size calculation overflows, resulting in an undersized buffer allocation. Subsequent decompression of the compressed payload then causes memory corruption [2][4]. No user interaction or prior authentication is necessary [3].

Impact

Successful exploitation causes memory corruption, leading to a crash of the pacemaker-based process and resulting in a denial of service (DoS) [1][2]. This can disrupt cluster management and potentially trigger node fencing, affecting service availability. The CVSS vector listed in reference [3] (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H) indicates Low confidentiality and integrity impact in addition to High availability impact, though the primary reported outcome is a denial of service [2].

Mitigation

As of the publication date (2026-06-16), no fixed package has been released. Patches are available in pull request #4128 on the ClusterLabs/pacemaker GitHub repository [4], which corrects the integer overflow by adding proper bounds checks and preventing overflow during the size computation. Workarounds include disabling the CIB remote listener by removing remote-clear-port or remote-tls-port from the cluster configuration if the feature is not required, as it is not enabled by default [2][3]. Users should monitor the Pacemaker project for updated releases. This CVE is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of this writing.

AI Insight generated on Jun 16, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

2
3e2c5e204284

Merge pull request #4128 from clumens/remote-overflow

https://github.com/ClusterLabs/pacemakerChris LumensJun 16, 2026via body-scan
3 files changed · +96 12
  • include/crm/common/remote_internal.h+3 0 modified
    @@ -29,6 +29,9 @@
     extern "C" {
     #endif
     
    +// 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 {
    
  • lib/cib/cib_remote.c+0 1 modified
    @@ -258,7 +258,6 @@ cib_remote_callback_dispatch(void *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;
    
  • lib/common/remote.c+93 11 modified
    @@ -24,7 +24,7 @@
     #include <netdb.h>
     #include <stdlib.h>
     #include <errno.h>
    -#include <inttypes.h>   // PRIx32
    +#include <inttypes.h>   // PRIu32, PRIx32
     
     #include <glib.h>
     #include <bzlib.h>
    @@ -66,6 +66,7 @@ static struct remote_header_v0 *
     localized_remote_header(pcmk__remote_t *remote)
     {
         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))) {
    @@ -100,11 +101,36 @@ localized_remote_header(pcmk__remote_t *remote)
     
         // Sanity checks
         if (header->payload_offset != sizeof(struct remote_header_v0)) {
    +        pcmk__err("Header payload offset %" PRIu32 " does not have expected "
    +                  "size %zu", header->payload_offset,
    +                  sizeof(struct remote_header_v0));
             return NULL;
         }
    -    if ((header->payload_offset
    -         + header->payload_compressed
    -         + header->payload_uncompressed) != header->size_total) {
    +
    +    if (header->payload_compressed != 0) {
    +        if (header->payload_compressed > (SIZE_MAX - header->payload_offset)) {
    +            pcmk__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)) {
    +            pcmk__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) {
    +        pcmk__err("Header total size %" PRIu32 " does not match calculated "
    +                  "size %zu", header->size_total, expected_size);
             return NULL;
         }
     
    @@ -254,6 +280,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) {
    +        pcmk__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);
    @@ -262,6 +295,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;
    @@ -289,16 +323,58 @@ 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(header->payload_offset + size_u, sizeof(char));
    +        unsigned int size_u = 0;
    +        char *uncompressed = NULL;
    +        size_t buffer_size = 0;
    +
    +#if (UINT32_MAX < UINT_MAX)
    +        if (header->payload_uncompressed >= UINT_MAX) {
    +            pcmk__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
    +            pcmk__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;
    +        }
     
    -        pcmk__trace("Decompressing message data %d bytes into %d bytes",
    -                    header->payload_compressed, size_u);
    +        buffer_size = (size_t) header->payload_offset + size_u;
    +        if (buffer_size > PCMK__REMOTE_MSG_MAX_SIZE) {
    +            pcmk__err("Message size %zu is larger than max allowed %u bytes",
    +                      buffer_size, PCMK__REMOTE_MSG_MAX_SIZE);
    +            return NULL;
    +        }
    +
    +        pcmk__trace("Decompressing message data %" PRIu32 " bytes into %u "
    +                    "bytes", header->payload_compressed, size_u);
    +
    +        uncompressed = pcmk__assert_alloc(buffer_size, 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);
    @@ -449,6 +525,12 @@ pcmk__read_available_remote_data(pcmk__remote_t *remote)
             read_len = header->size_total;
         }
     
    +    if (read_len > PCMK__REMOTE_MSG_MAX_SIZE) {
    +        pcmk__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;
    
e8fc5ac7e615

Merge c49b26cb1e11bb160198a28ac5567b0154b49121 into 239c456cb737e007a72597c0a6601d72acfc9e70

https://github.com/ClusterLabs/pacemakerChris LumensJun 16, 2026via nvd-ref
3 files changed · +96 12
  • include/crm/common/remote_internal.h+3 0 modified
    @@ -29,6 +29,9 @@
     extern "C" {
     #endif
     
    +// 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 {
    
  • lib/cib/cib_remote.c+0 1 modified
    @@ -258,7 +258,6 @@ cib_remote_callback_dispatch(void *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;
    
  • lib/common/remote.c+93 11 modified
    @@ -24,7 +24,7 @@
     #include <netdb.h>
     #include <stdlib.h>
     #include <errno.h>
    -#include <inttypes.h>   // PRIx32
    +#include <inttypes.h>   // PRIu32, PRIx32
     
     #include <glib.h>
     #include <bzlib.h>
    @@ -66,6 +66,7 @@ static struct remote_header_v0 *
     localized_remote_header(pcmk__remote_t *remote)
     {
         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))) {
    @@ -100,11 +101,36 @@ localized_remote_header(pcmk__remote_t *remote)
     
         // Sanity checks
         if (header->payload_offset != sizeof(struct remote_header_v0)) {
    +        pcmk__err("Header payload offset %" PRIu32 " does not have expected "
    +                  "size %zu", header->payload_offset,
    +                  sizeof(struct remote_header_v0));
             return NULL;
         }
    -    if ((header->payload_offset
    -         + header->payload_compressed
    -         + header->payload_uncompressed) != header->size_total) {
    +
    +    if (header->payload_compressed != 0) {
    +        if (header->payload_compressed > (SIZE_MAX - header->payload_offset)) {
    +            pcmk__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)) {
    +            pcmk__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) {
    +        pcmk__err("Header total size %" PRIu32 " does not match calculated "
    +                  "size %zu", header->size_total, expected_size);
             return NULL;
         }
     
    @@ -254,6 +280,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) {
    +        pcmk__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);
    @@ -262,6 +295,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;
    @@ -289,16 +323,58 @@ 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(header->payload_offset + size_u, sizeof(char));
    +        unsigned int size_u = 0;
    +        char *uncompressed = NULL;
    +        size_t buffer_size = 0;
    +
    +#if (UINT32_MAX < UINT_MAX)
    +        if (header->payload_uncompressed >= UINT_MAX) {
    +            pcmk__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
    +            pcmk__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;
    +        }
     
    -        pcmk__trace("Decompressing message data %d bytes into %d bytes",
    -                    header->payload_compressed, size_u);
    +        buffer_size = (size_t) header->payload_offset + size_u;
    +        if (buffer_size > PCMK__REMOTE_MSG_MAX_SIZE) {
    +            pcmk__err("Message size %zu is larger than max allowed %u bytes",
    +                      buffer_size, PCMK__REMOTE_MSG_MAX_SIZE);
    +            return NULL;
    +        }
    +
    +        pcmk__trace("Decompressing message data %" PRIu32 " bytes into %u "
    +                    "bytes", header->payload_compressed, size_u);
    +
    +        uncompressed = pcmk__assert_alloc(buffer_size, 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);
    @@ -449,6 +525,12 @@ pcmk__read_available_remote_data(pcmk__remote_t *remote)
             read_len = header->size_total;
         }
     
    +    if (read_len > PCMK__REMOTE_MSG_MAX_SIZE) {
    +        pcmk__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;
    

Vulnerability mechanics

Root cause

"Integer overflow in size calculations before memory allocation in the remote message decompression path allows buffer under-allocation and subsequent out-of-bounds write (CWE-190)."

Attack vector

An unauthenticated attacker with network reachability to a CIB remote listener (configured with `remote-port` or `remote-tls-port`) can send a specially crafted compressed remote message before authentication completes [ref_id=1]. The message header contains attacker-controlled `payload_offset`, `payload_uncompressed`, and `payload_compressed` fields that are used in size calculations without overflow safety checks [ref_id=1]. By choosing values that cause integer wraparound in these calculations, the attacker can trigger an undersized memory allocation followed by an out-of-bounds write during decompression, leading to service crash (denial of service) [CWE-190, ref_id=1].

Affected code

The vulnerability resides in `lib/common/remote.c` in the function `pcmk__remote_message_xml()`. The vulnerable code path is reachable pre-authentication through the CIB remote listener defined in `daemons/based/based_remote.c` [ref_id=1]. The patch also adds a new maximum size constant `PCMK__REMOTE_MSG_MAX_SIZE` in `include/crm/common/remote_internal.h` and cleans up a Coverity annotation in `lib/cib/cib_remote.c`.

What the fix does

The patch in `lib/common/remote.c` adds overflow-safe arithmetic checks before using header fields in size computations [patch_id=6191481]. In `localized_remote_header()`, it verifies that `payload_offset + payload_compressed` (or `payload_offset + payload_uncompressed`) does not wraparound via `SIZE_MAX` comparisons and then validates the result against `size_total`. In `pcmk__remote_message_xml()`, it guards the decompression allocation with similar bounds checks and introduces a maximum message size constant (`PCMK__REMOTE_MSG_MAX_SIZE`) enforced before decompression. The sender side is also hardened by checking that the sum of iov lengths does not exceed `UINT32_MAX`.

Preconditions

  • configCIB remote listener must be enabled via `remote-port` or `remote-tls-port` configuration
  • networkAttacker must have network reachability to the CIB remote listener port
  • authNo authentication is required — the vulnerable parsing occurs before `cib_remote_auth()` completes
  • inputAttacker must send a crafted remote message header with values that cause integer overflow in size calculations

Generated on Jun 16, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.