Repository navigation
fix(kafka): bound telemetry and Produce response arrays - #5580
Jeremy Schoemaker (shoemoney) wants to merge 1 commit into
Conversation
|
🎉 All Contributor License Agreements have been signed. Ready to merge. |
There was a problem hiding this comment.
Pull request overview
This PR hardens rd_kafka_handle_GetTelemetrySubscriptions() response parsing to prevent a pre-auth SIGSEGV by bounding the decoded array count, rejecting nullable/negative array counts, and handling allocation failures during parsing.
Changes:
- Cap
AcceptedCompressionTypesarray count parsing at 1000 (aligning with other capped array parsing in the file). - Reject negative (nullable) array counts for
AcceptedCompressionTypesviard_kafka_buf_parse_fail(). - Add an allocation failure check around
rd_calloc()foraccepted_compression_types.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| rk->rk_telemetry.accepted_compression_types_cnt = arraycnt; | ||
| rk->rk_telemetry.accepted_compression_types = | ||
| rd_calloc(arraycnt, sizeof(rd_kafka_compression_t)); | ||
| if (!rk->rk_telemetry.accepted_compression_types) | ||
| rd_kafka_buf_parse_fail( |
| rd_kafka_buf_read_arraycnt(rkbuf, &arraycnt, 1000); | ||
| if (arraycnt < 0) | ||
| rd_kafka_buf_parse_fail(rkbuf, | ||
| "ApiArrayCnt %" PRId32 " out of range", | ||
| arraycnt); |
|
Addressed in 5214a83: allocation-failure path no longer leaves accepted_compression_types_cnt set with a NULL pointer (temp pointer assigned after alloc succeeds, same for the arraycnt==0 branch), and added unit tests for arraycnt -1 and >1000 asserting rkbuf_err is set to RD_KAFKA_RESP_ERR__BAD_MSG with no crash. |
Kaushik Raina (k-raina)
left a comment
There was a problem hiding this comment.
Thanks for PR Jeremy Schoemaker (@shoemoney)
I have left few comments
|
|
||
| rd_kafka_buf_read_arraycnt(rkbuf, &arraycnt, 1000); | ||
|
|
||
| if (arraycnt) { |
There was a problem hiding this comment.
Do we need to check for negative error count here?
Similiar to https://github.com/confluentinc/librdkafka/pull/5580/changes#diff-81d5315dc871bcfe090e0b28790b8e6ff1b929209cd1d9b3bf03d3d96d7e89faR6831-R6832
| } | ||
|
|
||
| rk->rk_telemetry.accepted_compression_types_cnt = arraycnt; | ||
| rk->rk_telemetry.accepted_compression_types = |
There was a problem hiding this comment.
Should this memory be freed in case of https://github.com/confluentinc/librdkafka/pull/5580/changes#diff-81d5315dc871bcfe090e0b28790b8e6ff1b929209cd1d9b3bf03d3d96d7e89faR6845 fails?
…raycnt Two findings from @k-raina's review on confluentinc#5580. The temp pointer introduced last round leaks. rd_kafka_buf_read_i8 in the fill loop expands to rd_kafka_buf_parse_fail on a short buffer, which does goto err_parse, and that label has no free; the array only reached rk_telemetry after the loop, so the sole owner went out of scope. Assigning the pointer and count immediately after a successful rd_calloc puts it back under rd_kafka_telemetry_clear()'s free while still never pairing a non-zero _cnt with a NULL pointer, which is what the temp pointer was for. The RequestedMetrics arraycnt had no negative guard. rd_kafka_buf_read_arraycnt only rejects < -1 and > maxval, so a nullable -1 survives, and -1 is truthy, so the following if (arraycnt) reached rd_calloc(-1, sizeof(char *)). Same shape as the AcceptedCompressionTypes count this PR already guards, same fix.
|
All four are right. Three are fixed in Should this memory be freed if the read at :6845 fails? Yes, and it was a leak I introduced last round. Do we need a negative check at :6888? Yes, same bug. Should we make the same change at :3988? Yes, and that call passes Will rd_calloc abort on allocation failure? Only where asserts are live. |
|
Since your The sweep is only worth quoting because it detects the known positives. Run against Against the current head it reports none. Two limits worth stating: it only looks 8 lines past the read, and only for |
Kaushik Raina (k-raina)
left a comment
There was a problem hiding this comment.
Thanks Jeremy Schoemaker (@shoemoney) for addressing comments. Overall PR looks good,
Provided one comment.
| @@ -3986,6 +3986,10 @@ rd_kafka_handle_Produce_parse(rd_kafka_broker_t *rkb, | |||
| int32_t RecordErrorsCnt; | |||
| rd_kafkap_str_t ErrorMessage; | |||
| rd_kafka_buf_read_arraycnt(rkbuf, &RecordErrorsCnt, -1); | |||
There was a problem hiding this comment.
Should we update &RecordErrorsCnt also to upper limit?
There was a problem hiding this comment.
Yes. 4a0bd30c caps RecordErrorsCnt at the number of records in the request batch, so the bound follows the actual request instead of imposing a fixed limit on valid batches. The negative-count guard remains.
Added Produce v8/v9 parser tests for counts -1, 0, the batch size and batch size + 1. The over-limit case failed before the fix and passes now. Rebased onto current master; the build, all 25 built-in unit suites and clang-format 18 checks pass locally (SSL/SASL/curl disabled; no live Kafka cluster tests).
|
/sem-approve |
dce1ca7 to
4a0bd30
Compare
…raycnt Two findings from @k-raina's review on confluentinc#5580. The temp pointer introduced last round leaks. rd_kafka_buf_read_i8 in the fill loop expands to rd_kafka_buf_parse_fail on a short buffer, which does goto err_parse, and that label has no free; the array only reached rk_telemetry after the loop, so the sole owner went out of scope. Assigning the pointer and count immediately after a successful rd_calloc puts it back under rd_kafka_telemetry_clear()'s free while still never pairing a non-zero _cnt with a NULL pointer, which is what the temp pointer was for. The RequestedMetrics arraycnt had no negative guard. rd_kafka_buf_read_arraycnt only rejects < -1 and > maxval, so a nullable -1 survives, and -1 is truthy, so the following if (arraycnt) reached rd_calloc(-1, sizeof(char *)). Same shape as the AcceptedCompressionTypes count this PR already guards, same fix.
|
/sem-approve |
|
The CLA is signed and fully executed on Confluent's side as of 2026-09-19 02:02 UTC — |
Kaushik Raina (k-raina)
left a comment
There was a problem hiding this comment.
Jeremy Schoemaker (@shoemoney) Thanks for changes.
Left one comment on checks for requested_metrics & record_errors.
There was a problem hiding this comment.
Should we have change for NULL-check the alloc and Temp-pointer ownership-timing fix to requested_metrics & record_errors too, if applicable?
|
Good catch. Applied to both, though they needed slightly different treatment.
Ran the full in-process unit suite after the change, with the |
|
/sem-approve |
| memory (#5580, @shoemoney). | ||
|
|
||
| * Guard the Produce response `record_errors` allocation, and the | ||
| GetTelemetrySubscriptions `requested_metrics` allocation, against |
There was a problem hiding this comment.
nit: we should mention accepted_compression_types also.
There was a problem hiding this comment.
Added accepted_compression_types to the changelog entry in 6921423f. Squashed and rebased onto c58a8dc4 as CONTRIBUTING requests.
Both clean-base and updated builds and all 25 built-in unit suites passed locally; clang-format 18 passed. SSL/SASL/curl were disabled; broker integration suites were not run. The new head has no code-test CI result yet.
Validate telemetry array counts and Produce record-error counts before allocation. Guard allocation failures and establish cleanup ownership before parsing array elements. Add parser regression coverage.
f80ed91 to
6921423
Compare
|
/sem-approve |
Kaushik Raina (k-raina)
left a comment
There was a problem hiding this comment.
Thanks Jeremy Schoemaker (@shoemoney) for PR and congratulations on your first contribution to this repo.
We will wait for second reviewer to approve, before merging PR.
Ankith L (Ankith-Confluent)
left a comment
There was a problem hiding this comment.
Thanks for the fix Jeremy Schoemaker (@shoemoney)!
One thing on the CHANGELOG, the entries are under v2.15.1, which has already been released. Could you move them to a new v2.16.1 section at the top of the file.
Validate telemetry array counts and Produce record-error counts before allocation. Guard allocation failures and establish cleanup ownership before parsing array elements. Add parser regression coverage.
6921423 to
b7b915d
Compare
|
Moved both entries unchanged to a new Both clean master and the updated PR build and pass all 25 built-in unit suites. Changelog placement/text assertions, |
Ankith L (Ankith-Confluent)
left a comment
There was a problem hiding this comment.
Thanks for the PR Jeremy Schoemaker (@shoemoney)
LGTM!
Validate telemetry array counts and Produce record-error counts before allocation. Guard allocation failures and establish cleanup ownership before parsing array elements. Add parser regression coverage.
b7b915d to
70b6542
Compare
|
/sem-approve |


Malformed GetTelemetrySubscriptions array counts can reach allocation as negative or unbounded sizes. The Produce v8+ RecordErrors count also allowed any positive value, even when it exceeded the number of records sent.
This bounds accepted compression types at 1000, rejects nullable counts for both telemetry arrays, and bounds Produce record errors by the request batch size. Telemetry allocation ownership is established before reading array elements so a truncated response is cleaned up correctly.
Validation on macOS arm64:
Configured with SSL, SASL and curl disabled. No live Kafka cluster tests were run.