Skip to content

Fix missing null-terminator content check in FlexBuffers VerifyTerminator#9086

Open
Alearner12 wants to merge 1 commit into
google:masterfrom
Alearner12:flexbuffers-verify-terminator-content
Open

Fix missing null-terminator content check in FlexBuffers VerifyTerminator#9086
Alearner12 wants to merge 1 commit into
google:masterfrom
Alearner12:flexbuffers-verify-terminator-content

Conversation

@Alearner12
Copy link
Copy Markdown

@Alearner12 Alearner12 commented May 9, 2026

Fixes #9085
Sibling fix to #9072 ("Fix logic inversion in FlexBuffers VerifyKey()", commit a6979fe).

Summary

flexbuffers::Verifier::VerifyTerminator() is intended to verify that an FBT_STRING value has a valid null terminator at offset size, but the current implementation only verifies that the byte at that offset lies inside the buffer; it does not check that the byte equals 0x00.

// include/flatbuffers/flexbuffers.h, current code:
bool VerifyTerminator(const String& s) {
  return VerifyFromPointer(reinterpret_cast<const uint8_t*>(s.c_str()),
                           s.size() + 1);
}

VerifyFromPointer(p, size + 1) only confirms that the range [p, p + size + 1) lies within buf_. The terminator byte's value is never read.

As a result, VerifyBuffer() accepts FlexBuffers whose strings are not actually null-terminated. A subsequent call to AsString().c_str() followed by strlen() / strcmp() / any C-API call that expects a NUL-terminated string then reads past the verified region.

Why this is the same bug class as #9072

#9072 fixed VerifyKey(), which had the same shape: the verifier accepted a key whose terminator was missing, and downstream strlen()/strcmp() then ran past the buffer. VerifyTerminator() is the FBT_STRING equivalent helper, in the same class, called from the same VerifyRef() switch (case FBT_STRING:).

Compare to the non-flex VerifyString() in include/flatbuffers/verifier.h, which gets it right by also checking the terminator byte's value:

bool VerifyString(const String* const str) const {
  size_t end;
  return !str || (VerifyVectorOrString<uoffset_t>(...) &&
                  Verify(end, 1) &&
                  Check(buf_[end] == '\0'));   // <-- content check
}

This PR aligns the FlexBuffers verifier with the non-flex one.

Fix

bool VerifyTerminator(const String& s) {
  const uint8_t* p = reinterpret_cast<const uint8_t*>(s.c_str());
  // First make sure the terminator byte is in-buffer, then check it is 0.
  // Mirrors the non-flex VerifyString() in verifier.h.
  return VerifyFromPointer(p, s.size() + 1) && p[s.size()] == 0;
}

Test

Adds FlexBuffersVerifyStringTerminatorTest() in tests/flexbuffers_test.cpp. The test constructs a hand-rolled flexbuffer with an FBT_STRING whose terminator byte is 'X' (non-zero) and asserts VerifyBuffer() returns false. Without this PR, the same test fails (verifier returns true).

I also confirmed under AddressSanitizer that, on the unpatched tree, the buffer is accepted by VerifyBuffer() and a follow-up strlen(root.AsString().c_str()) triggers a heap-buffer-overflow read; with this PR the buffer is rejected and the OOB read does not occur.

@Alearner12 Alearner12 requested a review from dbaileychess as a code owner May 9, 2026 15:23
@github-actions github-actions Bot added the c++ label May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing Null-Terminator Content Check in VerifyTerminator()

1 participant