Skip to content

reflection: bounds-check the union type slot in the verifier#9122

Open
evilgensec wants to merge 2 commits into
google:masterfrom
evilgensec:fix/reflection-verifyvector-union-typevec
Open

reflection: bounds-check the union type slot in the verifier#9122
evilgensec wants to merge 2 commits into
google:masterfrom
evilgensec:fix/reflection-verifyvector-union-typevec

Conversation

@evilgensec

@evilgensec evilgensec commented Jun 4, 2026

Copy link
Copy Markdown

Problem

reflection::Verify is the runtime verifier for untrusted reflection-encoded FlatBuffers. When it verifies a union it reads the union type slot from the vtable without bounds-checking the voffset first, unlike the sibling value slot and every other field:

  • VerifyVector (vector of unions) reads the type vector at vec_field.offset() - sizeof(voffset_t) via GetPointer with no VerifyField (4-byte read, up to ~64 KB past the buffer).
  • VerifyObject (scalar union) reads the type byte at field_def->offset() - sizeof(voffset_t) via GetField<uint8_t> with no VerifyField (1-byte read).

A union value field sorts before its hidden type field, so the verifier reaches the unvalidated type slot first. A malformed buffer with a poisoned type voffset makes Verify read out of bounds. Both confirmed with ASAN.

Fix

Add the missing VerifyField guard in both branches, matching the existing value-slot and string-field checks. Add a reflection_test.cpp regression test that builds a schema plus a malformed buffer for the vector-of-unions and scalar-union cases and checks Verify rejects each instead of reading out of bounds.

VerifyVector reads the union type-vector slot at vec_field.offset() - sizeof(voffset_t) via GetPointer with no VerifyField, while the value-vector slot is verified. flatbuffers::Verify can therefore dereference an out-of-range vtable offset on a malformed buffer. Add the same VerifyField the value path uses before reading the type vector.
@evilgensec evilgensec requested a review from dbaileychess as a code owner June 4, 2026 09:54
@github-actions github-actions Bot added c++ codegen Involving generating code from schema labels Jun 4, 2026
VerifyObject's union branch read the union type byte via GetField<uint8_t>
at offset() - sizeof(voffset_t) with no VerifyField, the same gap fixed
for the union type vector in VerifyVector. A union value field sorts
before its hidden type field, so the verifier reaches the unvalidated
type slot first, and a poisoned vtable offset caused an out-of-bounds
read. Guard it the same way the value field is guarded.

Add a reflection_test.cpp regression test that builds a malformed buffer
for both a vector of unions and a scalar union and checks Verify rejects
each instead of reading out of bounds.
@evilgensec evilgensec changed the title reflection: bounds-check the union type-vector field in VerifyVector reflection: bounds-check the union type slot in the verifier Jun 5, 2026
@evilgensec

Copy link
Copy Markdown
Author

@dbaileychess this is ready when you have a moment.

reflection::Verify reads the union type slot from the vtable without bounds-checking the voffset first: the type vector in VerifyVector and the type byte in VerifyObject, unlike the sibling value slot and every other field. A union value field sorts before its hidden type field, so the verifier reaches the unvalidated type slot first. A malformed buffer with a poisoned type voffset makes Verify read out of bounds (4 bytes, up to ~64 KB past the buffer, for the vector case; 1 byte for the scalar case). Both confirmed with ASAN.

The change adds the missing VerifyField guard in both branches, matching the existing value-slot and string checks, plus a reflection_test.cpp regression test covering the vector-of-unions and scalar-union cases. Full test suite passes under ASAN locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ codegen Involving generating code from schema

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant