Skip to content

Fix integer overflow in vendored json-parser allocation size - #1296

Open
Ion (Socialpranker) wants to merge 2 commits into
confluentinc:8.2.0-postfrom
Socialpranker:fix/json-overflow-guard
Open

Fix integer overflow in vendored json-parser allocation size#1296
Ion (Socialpranker) wants to merge 2 commits into
confluentinc:8.2.0-postfrom
Socialpranker:fix/json-overflow-guard

Conversation

@Socialpranker

Copy link
Copy Markdown

This patch was written by Claude Sonnet 5 (Anthropic); I reviewed the change and the reasoning before submitting.

clients/cloud/c/json.c vendors the json-parser library. In new_value(), the array and object branches multiply value->u.array.length and value->u.object.length by sizeof(json_value*) and sizeof(json_object_entry) respectively before passing the result to json_alloc(). Neither multiplication is checked for overflow first. If it wraps, json_alloc() receives a small size, the allocation succeeds, and the parser then writes length elements into a buffer that is too small for them, a heap buffer overflow driven entirely by the JSON input being parsed.

json_alloc() does guard state->used_memory against wrapping via state->ulong_max, but that check only runs after the multiplication has already happened, so it cannot catch an overflow in the multiplication itself.

The fix adds a bound check in both branches before the multiplication, using the same state->ulong_max field json_alloc() already computes for the same purpose, so the guard follows the existing convention in the file rather than introducing a new one. On failure it returns 0, which is exactly how allocation failure is already signaled everywhere else in new_value(). The diff is intentionally limited to these two checks, no other code in the file was touched.

I compiled clients/cloud/c/json.c standalone with gcc -Wall -Wextra -std=c99 before and after the change; the warning output is identical (two pre-existing unused-parameter warnings in default_alloc/default_free, unrelated to this fix). I did not build consumer.c/producer.c through the Makefile because that requires librdkafka, which isn't installed in the environment I used, but json.c has no dependency on librdkafka and compiles cleanly on its own.

new_value() in clients/cloud/c/json.c multiplies value->u.array.length
and value->u.object.length by sizeof(json_value*) / sizeof(json_object_entry)
before passing the result to json_alloc(), with no check that the
multiplication itself fits in the allocator's size type. On a platform
where this overflows, the allocation comes back undersized while the
parser still writes array.length / object.length elements into it,
producing a heap buffer overflow from attacker-controlled JSON input.

json_alloc() already guards against overflowing the running memory
total via state->ulong_max, but that only helps once the multiplied
size is passed in - it doesn't stop the multiplication from wrapping
first. This adds an explicit bound check before each multiplication,
using the same state->ulong_max the file already computes for this
purpose, matching the existing style.

Verified: reviewed clients/cloud/c/json.c against upstream
json-parser/json-parser, where this exact class of overflow was fixed
via a similar bounds check. Compiled clients/cloud/c/json.c standalone
with gcc -Wall -Wextra; no new warnings versus the unpatched file.
@Socialpranker
Ion (Socialpranker) requested a review from a team as a code owner August 10, 2026 12:35
The previous guard compared length against ulong_max, but values_size
that stores the multiplication result is declared as a plain int, so
a length between INT_MAX/sizeof(entry) and ulong_max/sizeof(entry)
still overflows values_size itself before the ulong_max check would
ever catch it. Guard against INT_MAX/sizeof(entry) instead, matching
the type that actually holds the product.
@Socialpranker

Copy link
Copy Markdown
Author

Pushed a follow-up commit: the object-branch guard checked against ulong_max, but values_size that stores the multiplication result is declared as a plain int. A length between INT_MAX/sizeof(entry) and ulong_max/sizeof(entry) would pass that guard and still overflow values_size itself. Fixed to guard against INT_MAX/sizeof(entry) instead, matching the type that actually holds the product. The array branch was already correct since it allocates directly with no intermediate int.

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