Skip to content

event_rabbitmq: fix dupl_string() NUL-inclusive len corrupting AMQP shortstr#3834

Open
NormB wants to merge 1 commit intoOpenSIPS:masterfrom
NormB:fix/event-rabbitmq-dupl-string-nul
Open

event_rabbitmq: fix dupl_string() NUL-inclusive len corrupting AMQP shortstr#3834
NormB wants to merge 1 commit intoOpenSIPS:masterfrom
NormB:fix/event-rabbitmq-dupl-string-nul

Conversation

@NormB
Copy link
Member

@NormB NormB commented Mar 4, 2026

Summary

dupl_string() in event_rabbitmq.c increments dst->len after NUL-terminating the unescaped string (dst->len++), causing .len to include the trailing NUL byte. This makes amqp_basic_publish() encode exchange and routing-key as AMQP shortstr fields with an extra 0x00 byte, breaking broker routing.

The bug was introduced in commit 0260de0c1 (2014) when dupl_string() was refactored to use un_escape(). The original memcpy-based implementation had the same off-by-one (dst->len = end - begin + 1).

Details

AMQP 0-9-1 encodes exchange and routing-key as shortstr fields (1-byte length prefix + N content bytes). With the inflated .len, the broker receives a shortstr containing the field value followed by 0x00, which fails to match the declared exchange or creates one with an embedded NUL.

Over time, several - 1 compensations were added downstream to work around the inflated length:

  • tls_dom_name.len-- in rmq_parse() (commit 472ae546c, TLS support)
  • address.len - 1, exchange.len - 1, routing_key.len - 1 in rmq_print()
  • strlen(user) - 1 in rmq_print() — this one was wrong even pre-patch since strlen() never counts the NUL

Solution

Remove the dst->len++ in dupl_string() and all downstream compensations. Three additional issues fixed while testing:

  1. Memory leak: dupl_string() calls shm_malloc before un_escape(), but on un_escape() failure the buffer was never freed. Added shm_free + cleanup.
  2. Default user NUL termination: shm_malloc(rmq_static_holder.len) + memcpy(..., len) for "guest" didn't copy the NUL terminator. The pointer is passed to amqp_login()strlen(). Changed to len + 1.
  3. strlen(user) - 1: The /* skip 0 */ comment reveals the author believed strlen() includes the NUL. It doesn't — this always truncated the last character of the username in rmq_print() output.

Tested with:

  • AMQP wire capture (tshark) confirming correct shortstr encoding
  • TLS domain name lookup (instrumented debug + regression simulation)
  • Standalone test harness (1019 lines) with 100K-iteration stress test confirming shm leak fix

Compatibility

No backward compatibility issues. All behavioral changes are strictly correctional:

  • Wire encoding: NUL byte no longer included (fixes the reported bug)
  • rmq_match(): symmetric — both sides were inflated equally, no change
  • rmq_print(): compensations removed, output unchanged
  • amqp_login() user/password: uses char* via strlen(), unaffected

Closing issues

Closes #3828

…hortstr

dupl_string() incremented dst->len after NUL-terminating the unescaped
string, causing .len to include the trailing NUL byte. This made
amqp_basic_publish() encode exchange and routing-key shortstr fields
with an extra 0x00, breaking broker routing.

Remove the len++ and all downstream compensations (tls_dom_name.len--,
and the - 1 adjustments in rmq_print() for address, exchange, routing
key, and user). Also fix the un_escape() error path to free the
already-allocated shm buffer, and fix the default-user allocation to
explicitly NUL-terminate.

Closes OpenSIPS#3828
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] OpenSIPS 3.6.3: AMQP shortstr length includes trailing NUL (exchange ends with \x00)

1 participant