Skip to content

Fix integer wraparound in Buffer bounds checks on 32-bit platforms#193

Merged
garretrieger merged 1 commit into
google:masterfrom
sharadboni:fix-buffer-integer-wraparound
Apr 16, 2026
Merged

Fix integer wraparound in Buffer bounds checks on 32-bit platforms#193
garretrieger merged 1 commit into
google:masterfrom
sharadboni:fix-buffer-integer-wraparound

Conversation

@sharadboni
Copy link
Copy Markdown
Contributor

Summary

The typed Read methods in Buffer (ReadU8, ReadU16, ReadU24, ReadU32, ReadTag, ReadR64) use bounds checks of the form offset_ + N > length_. On 32-bit platforms where size_t is 32 bits, if offset_ is close to SIZE_MAX (e.g. 0xFFFFFFFF), the addition offset_ + N wraps around to a small value, bypassing the bounds check and allowing out-of-bounds reads from the underlying buffer.

Root cause: unsigned integer wraparound in offset_ + N when offset_ is near SIZE_MAX on ILP32 platforms.

Fix: Restructure all bounds checks from offset_ + N > length_ to length_ < N || offset_ > length_ - N, which avoids any arithmetic that could overflow. This pattern is consistent with the existing Read(uint8_t*, size_t) method which already uses the safe offset_ > length_ - n_bytes form.

Additionally, set_offset() previously accepted any value without validation. This is changed to return bool and reject offsets beyond the buffer length. The sole caller in font.cc (TTC collection font parsing) now checks the return value.

Changes

  • src/buffer.h: Fix bounds checks in ReadU8, ReadU16, ReadU24, ReadU32, ReadTag, ReadR64 to use overflow-safe comparisons
  • src/buffer.h: Change set_offset() from void to bool with bounds validation
  • src/font.cc: Check set_offset() return value when seeking to TTC font offsets

Impact

An attacker supplying a crafted WOFF2 font could potentially trigger out-of-bounds memory reads on 32-bit platforms (e.g., 32-bit Android, embedded systems, wasm32) by manipulating the buffer offset to near SIZE_MAX. This could lead to information disclosure or crashes.

Test plan

  • Verified the project builds cleanly with make
  • The fix is a minimal, mechanical change to comparison operand ordering
  • No functional change on 64-bit platforms (the wraparound is not practically reachable with 64-bit size_t)

The typed Read methods (ReadU8, ReadU16, ReadU24, ReadU32, ReadTag,
ReadR64) used bounds checks of the form `offset_ + N > length_`. On
32-bit platforms where size_t is 32 bits, if offset_ is close to
SIZE_MAX (e.g. 0xFFFFFFFF), the addition `offset_ + N` wraps around
to a small value, bypassing the bounds check and allowing out-of-bounds
reads from the underlying buffer.

Fix by restructuring the checks to `length_ < N || offset_ > length_ - N`,
which avoids any arithmetic that could overflow. This is consistent with
the existing Read(uint8_t*, size_t) method which already uses the safe
`offset_ > length_ - n_bytes` pattern.

Also add bounds validation to set_offset() (changing return type from
void to bool) to reject offsets beyond the buffer length, and update
the sole caller in font.cc to check the return value. This prevents
TTC font collection offsets from positioning the buffer past its end.
@sharadboni
Copy link
Copy Markdown
Contributor Author

@garretrieger Could you review this security fix? It addresses integer wraparound in Buffer bounds checks on 32-bit platforms that allows out-of-bounds reads via crafted TTC fonts.

@garretrieger garretrieger merged commit 1c69169 into google:master Apr 16, 2026
2 checks passed
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