Skip to content

Fix null dereference in JSON printer for unions without type field - #9308

Open
xhon-pelushi wants to merge 1 commit into
google:masterfrom
xhon-pelushi:fix-9033
Open

xhon-pelushi wants to merge 1 commit into
google:masterfrom
xhon-pelushi:fix-9033

Conversation

@xhon-pelushi

Copy link
Copy Markdown

Fixes #9033

When JsonPrinter::PrintOffset() (src/idl_gen_text.cpp) prints a union, it reads the discriminator through prev_val, a pointer that GenStruct() sets to the field printed just before the union. Corrupt buffers that have the union value but not its _type field broke this in two ways:

  1. Null. If no earlier field was present, prev_val is nullptr. The only guard was FLATBUFFERS_ASSERT(prev_val), which release builds compile out, so *prev_val segfaults. This is the crash in the issue: flatc --json --raw-binary on the 40-byte file exits with SIGSEGV on master.
  2. Stale. GenStruct() only updated prev_val for fields that were present. If the _type field is absent but an earlier field was present, prev_val still points at that earlier field, and its byte is silently read as the union type. For example, with table Root { a: ubyte; u: MyUnion; }, {a: 1, u_type: Inner, u: {x: 42}} and the u_type vtable slot zeroed, master prints { a: 1, u: { x: 42 } } with no error, using a as the type.

The fix:

  • In PrintOffset(), the assert is replaced with if (!prev_val) return "union type field not present";, which reports the problem through the existing error path (GenText() / GenTextFromTable() / GenTextFile() already return const char* errors).
  • In GenStruct(), prev_val is reset to nullptr when a field is absent, so a union only ever reads its own _type field.

Both parts are needed. With only the null check, the stale case in the new test still fails.

The new test is JsonUnionMissingTypeTest in tests/json_test.cpp. It builds a buffer from JSON, zeroes one vtable slot, and checks GenText(): the unmodified buffer still prints, and errors are returned when u_type is missing (with and without an earlier present field) and when v_type is missing for a vector of unions.

Testing (Release build, -DNDEBUG):

  • flattests: all tests pass with this change.
  • Master with only the new test applied: flattests segfaults inside JsonUnionMissingTypeTest → GenText → PrintOffset.
  • Master with only the null check plus the new test: the stale case fails (json_test.cpp:250).
  • The issue's flatc repro now prints Unable to generate text for corrupt (union type field not present) and exits with 1 instead of crashing. A valid buffer prints as before.

A corrupt buffer can hold a union value while its type field is absent.
PrintOffset() only guarded this with FLATBUFFERS_ASSERT, so release builds
dereferenced a null prev_val and crashed (e.g. `flatc --json` on such a
binary). If an earlier unrelated field was present, prev_val still pointed
at it and its value was silently used as the union type.

Return an error when prev_val is null, and reset prev_val for absent
fields so a union never picks up a stale type field.

Fixes google#9033
@github-actions github-actions Bot added c++ codegen Involving generating code from schema labels Oct 6, 2026

This branch has not been deployed

No deployments
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.

Null Pointer Dereference in JsonPrinter::PrintOffset() for Union Types

1 participant