Skip to content

Harden Base64 bounds and JSON parser nesting#268

Open
idrassi wants to merge 2 commits into
frang75:mainfrom
idrassi:security/encode-hardening
Open

Harden Base64 bounds and JSON parser nesting#268
idrassi wants to merge 2 commits into
frang75:mainfrom
idrassi:security/encode-hardening

Conversation

@idrassi

@idrassi idrassi commented May 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the encode library against memory-safety and stack-exhaustion bugs in Base64 size handling and the JSON parser.

Base64

The previous b64_encoded_size computed 4 * (data_size / 3) + 5 in 32-bit arithmetic. For sufficiently large data_size this overflowed uint32_t silently and returned an undersized length. Callers such as i_encode_from_data() then allocated a too-small buffer and called b64_encode, which wrote past the end of that buffer.

This PR:

  • computes encoded sizes with 64-bit arithmetic
  • returns 0 from b64_encoded_size when the computed size is not safely representable
  • intentionally rejects encoded-size results above UINT32_MAX - 1024, leaving a conservative 1024-byte safety margin below the uint32_t ceiling instead of allowing allocations and string buffers at the absolute representable limit
  • adds an always-on runtime check in b64_encode that returns 0 and writes an empty terminator when the caller's buffer is too small
  • adds b64_decode_ex, a bounds-checked decode API with an explicit destination size and *written out-parameter
  • makes b64_decode_ex preflight the decoded output size before writing, so failure from an undersized destination is non-partial
  • switches internal Base64 callers, including the JSON binary-field path, to the bounds-checked API
  • avoids internal allocations when encoded-size calculation fails

If JSON binary serialization encounters a binary payload whose Base64 output would exceed the accepted size range, it now emits null for that field and marks the output stream corrupt instead of silently serializing the value as an empty string.

JSON Parser

The JSON parser was mutually recursive across object, array, value, and skip/jump paths. Attacker-controlled JSON with sufficient nesting could exhaust the stack.

The parser's minus-token handling also used recursion: a long run of - tokens could recurse through i_new_token until stack exhaustion in release builds.

This PR:

  • adds an internal maximum JSON nesting depth of 256
  • enforces the limit on both parse and skip/jump paths
  • replaces recursive minus-token handling with an iterative loop
  • rejects repeated minus tokens and minus followed by a non-numeric token as invalid JSON

Compatibility

The existing b64_encode and b64_decode APIs remain available.
b64_decode_ex is added as the safer API for callers that need reliable bounds enforcement.

This PR doesn't add strict Base64 syntax or padding validation; malformed input validation remains separate from buffer bounds hardening.

@idrassi idrassi force-pushed the security/encode-hardening branch from cd6ef7a to 12eedc6 Compare May 18, 2026 05:08
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