Skip to content

Fix cluster id over-read past the controller id in Metadata response - #5545

Draft
pranav shah (prashah-confluent) wants to merge 2 commits into
masterfrom
dev_prashah_parse_cluster_id_fix
Draft

Fix cluster id over-read past the controller id in Metadata response#5545
pranav shah (prashah-confluent) wants to merge 2 commits into
masterfrom
dev_prashah_parse_cluster_id_fix

Conversation

@prashah-confluent

@prashah-confluent pranav shah (prashah-confluent) commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

The Metadata response parser copied the cluster_id string using strlen(),
even though the source is not nul-terminated. This causes a read past the end
of the string into the following controller_id field, producing a corrupted
(over-long) cluster id and, in the worst case, a heap over-read past the receive
buffer.

Root cause

In rd_kafka_parse_Metadata0() (src/rdkafka_metadata.c), cluster_id is read
with rd_kafka_buf_read_str(), which sets cluster_id.str to point into the
receive buffer
with a known cluster_id.len but no nul terminator. The
copy into the internal metadata struct was done via
rd_tmpabuf_write_str(&tbuf, cluster_id.str), which internally does
strlen(cluster_id.str) + 1.

On the wire, cluster_id is immediately followed by the controller_id int32.
So strlen() walks past the string into the controller_id bytes until it
happens to hit a 0x00. It only stops correctly by luck - when the leading
(big-endian, i.e. network-order) byte of controller_id is 0x00, which is the
case for typical small broker ids. It breaks when that byte is non-zero, e.g.:

  • controller_id = -1 (0xFFFFFFFF) — a valid "no controller" value, or
  • any controller_id >= 2^24.

In those cases the copied cluster id is too long/corrupted, and if no 0x00
appears before the end of the buffer the strlen() reads out of bounds.

Impact

  • The internal per-response cluster id (mdi->cluster_id) is corrupted. This is
    surfaced to applications via the Admin DescribeCluster API
    (rd_kafka_DescribeCluster_result_cluster_id()).
  • Potential heap over-read (memory-safety issue) when the following bytes
    contain no 0x00.
  • Note: rd_kafka_clusterid() is not affected — the cached rk_clusterid
    is built with RD_KAFKAP_STR_DUP (length-based) and remains correct.

Fix

Copy exactly cluster_id.len bytes and write our own nul terminator, instead of
relying on strlen():

Testing

  • Added a mock-based regression test do_test_cluster_id_not_overread() in tests/0146-metadata_mock.c. It forces the mock cluster's controller_id to -1 and asserts the cluster id surfaced by DescribeCluster (the code path under test) exactly matches the authoritative value from rd_kafka_clusterid().
  • Verified the test fails on the pre-fix code ("mockCluster…\xFF\xFF\xFF\xFF", len 25 vs expected len 20 — "cluster_id was over-read") and passes with the fix.
  • Added a small mock helper, rd_kafka_mock_set_controller_id(), to let tests control the reported controller id (additive, RD_EXPORT).

Compatibility

No public API/ABI changes to the client library; the fix is internal to metadata
parsing. The only added symbol is the additive mock helper
rd_kafka_mock_set_controller_id().

@confluent-cla-assistant

Copy link
Copy Markdown

🎉 All Contributor License Agreements have been signed. Ready to merge.
Please push an empty commit if you would like to re-run the checks to verify CLA status for all contributors.

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.

1 participant