Skip to content

Add integer overflow guards to upb buffer growth functions#26513

Open
kodareef5 wants to merge 2 commits intoprotocolbuffers:mainfrom
kodareef5:upb-overflow-hardening
Open

Add integer overflow guards to upb buffer growth functions#26513
kodareef5 wants to merge 2 commits intoprotocolbuffers:mainfrom
kodareef5:upb-overflow-hardening

Conversation

@kodareef5
Copy link
Copy Markdown
Contributor

upb_String_Append and jsondec_resize perform unchecked arithmetic during buffer growth that can overflow size_t:

upb/io/string.h — upb_String_Append

size_ + size can wrap on 32-bit platforms when the existing string is large and the append size is large. When it wraps, the capacity check at line 87 passes incorrectly (the wrapped sum is smaller than capacity), and memcpy writes past the allocated buffer. The subsequent 2 * (size_ + size) + 1 compounds the overflow.

Added two guards:

  • Check size > SIZE_MAX - s->size_ before the addition
  • Check sum > (SIZE_MAX - 1) / 2 before the doubling

On overflow, upb_String_Append returns false instead of corrupting memory.

upb/json/decode.c — jsondec_resize

2 * oldsize wraps to 0 when oldsize > SIZE_MAX / 2. UPB_MAX(8, 0) then selects 8, and the buffer is reallocated to 8 bytes while existing data may be much larger.

Added a guard: check oldsize > SIZE_MAX / 2 before doubling, and report an error via jsondec_err on overflow.

Tests

Added two tests to upb/io/string_test.cc:

  • AppendOverflowReturnsFailure — verifies that an append causing size_ + size to wrap returns false and leaves the string unchanged
  • AppendNormalStillWorks — verifies normal appends are unaffected

All existing tests pass (//upb/io:string_test, //upb/json:decode_test).

upb_String_Append (upb/io/string.h) and jsondec_resize (upb/json/decode.c)
perform unchecked arithmetic that can overflow size_t during buffer growth:

- upb_String_Append: size_ + size can wrap, causing the capacity check to
  pass incorrectly and memcpy to write past the buffer. The doubling
  2 * (size_ + size) + 1 can also wrap.

- jsondec_resize: 2 * oldsize can wrap to 0 when oldsize > SIZE_MAX/2,
  causing the buffer to shrink instead of grow.

Add overflow checks before the arithmetic in both functions. On overflow,
upb_String_Append returns false and jsondec_resize reports an error.

Add unit tests demonstrating that the overflow check fires correctly and
that normal operations are unaffected.
Two additional sites with unchecked buffer doubling:

- upb/reflection/desc_state.c:24-25: bufsize *= 2 without overflow
  check, plus unsigned underflow in oldbufsize - used comparison.
  Add used > oldbufsize guard and SIZE_MAX/2 check before doubling.

- upb/message/array.c:127: while loop doubling new_capacity without
  overflow check. If min_capacity is large, new_capacity wraps to 0.
  Add SIZE_MAX/2 check inside the loop.
@kodareef5
Copy link
Copy Markdown
Contributor Author

@themavik Thanks for the thorough review. Good catch on the jsondec_resize observation — jsondec_err is indeed noreturn (UPB_NORETURN), so size won't be used uninitialized in practice, but I can add a defensive initialization to 0 to be safe against future changes. Happy to push that if desired.

Regarding CI: the "Set Variables" / "All Blocking Tests (fork)" failure appears to be a fork permissions issue rather than a code problem — the workflow can't access required secrets from a fork. The actual test suite can't run in this context. If a maintainer can confirm or retrigger from the upstream side, that would help.

@karenwuz karenwuz added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 8, 2026
@github-actions github-actions Bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 8, 2026
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.

2 participants