Repository navigation
Conversation
VerifyTerminator range-checked size + 1 bytes but did not confirm the trailing byte is a NUL. A FlexBuffer with an honest length prefix and a non zero terminator passed VerifyBuffer, so a consumer that NUL scans the verified String::c_str() (strlen, strcmp, printf %s, std::string from c_str) read past the end of the buffer. Require the terminator to be NUL after the range check, matching flatbuffers::Verifier::VerifyString. Same class as the VerifyKey fix in a6979fe.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The FlexBuffers verifier checks that a string occupies
size + 1bytes inside the buffer but never checks that the trailing byte is a NUL. A FlexBuffer whose string length prefix is honest but whose terminator byte is non zero still passesflexbuffers::VerifyBuffer. Becauseflexbuffers::String::c_str()is a public accessor that callers treat as a C string, any consumer that NUL scans the verified string (strlen,strcmp,printf("%s"),std::stringfromc_str()) reads past the end of the buffer allocation.This is the same class as the recently merged
VerifyKeyfix (commit a6979fe). That change corrected key verification; the string value path throughVerifyTerminatorwas left incomplete.flatbuffers::Verifier::VerifyStringalready enforces the terminator (Check(buf_[end] == '\0')), so this aligns FlexBuffers with the existing FlatBuffers behavior.Affected code
include/flatbuffers/flexbuffers.h,VerifyTerminator(reached fromVerifyRefforFBT_STRING):VerifyFromPointer(..., s.size() + 1)only range checks thatsize + 1bytes are inside the buffer. It does not check the value of the last byte. By contrastVerifyKeyscans for a NUL and returns false when none is found, so keys are safe; the string value path is the outlier.Impact
Out of bounds read on a buffer that the application already verified. The verifier is the sole security boundary for FlexBuffers: once
VerifyBufferreturns true, accessors read with no further bounds checks. A consumer that only usessize()orstring_viewis unaffected; a consumer that NUL scansc_str()reads out of bounds, which exposes adjacent memory or crashes.c_str()is the natural string accessor, so the exposure is broad. Numeric accessors such asAsInt64stop at the non numeric tail and are not affected.Fix
After the range check succeeds, require the terminating byte to be NUL. The range check runs first and guarantees the byte at index
size()is in bounds, so reading it is safe:Reproduction
Built a legitimate FlexBuffer containing a one character string with
flexbuffers::Builder, flipped the in range terminator byte from0x00to0x42, confirmedVerifyBufferstill returned true, then calledstrlenon the verifiedc_str().Six byte buffer, built then one byte patched:
Before the fix, AddressSanitizer reports a heap buffer overflow READ at the
strlencall. After the fix:VerifyBufferreturns false, no ASAN error.VerifyBufferreturns true,c_str()reads "hello", length 5, no regression.