Fix type confusion and out-of-bounds read in reflection union verification - #9220
Open
Grolar1337 wants to merge 1 commit into
Open
Fix type confusion and out-of-bounds read in reflection union verification#9220Grolar1337 wants to merge 1 commit into
Grolar1337 wants to merge 1 commit into
Conversation
VerifyUnion treated the union discriminator as a vector position instead of matching the declared enum value, and read it at a fixed 8-bit width even for int32-backed unions; the resolved member's Type.index reached objects()->Get() with no bounds check, and no cross-validation of these indices against schema sizes. - VerifyUnion/VerifyObject: value-based member resolution, declared-width discriminator read, no-match rejected - SchemaIsValid(): cross-reference validation at Verify/Annotate entry - Bounds guards on all objects()/enums()->Get sites in reflection.cpp and binary_annotator - GetUnionType: null-safe bool overload (legacy overload preserved) - Regression test: reflection_union_security_test.cpp Signed-off-by: Grolar1337 <blkyakupsait@gmail.com>
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Author
|
I just submitted the CLA form, is that correct? |
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.
Fixes a reflection-verifier defect found during a security review of
attacker-supplied reflection schemas (
.bfbs) and data buffers consumedthrough
flatbuffers::reflection::Verify.Branch:
fix/reflection-union-verify(frommaster@ 5761d6e).Commit:
ca38ac5f— "Fix type confusion and OOB read in reflection union verification"Problem
VerifyUnion(src/reflection.cpp) resolved a union's concrete member typeby using the wire discriminator as a position into the union enum's
values()vector, instead of matching it against declared member values:Consequences:
values()position but not a declared value (e.g.
1forunion ABC : int { A = 555, B = 666 }) was verified against the wrong member. A B (string)payload tagged
1was accepted as member A.union enum's underlying width (int32 for the example above), but was read
as a hard-coded
uint8_t, so a legitimatetag == 555was read as byte43and rejected.VerifyUniondereferencedType.indexwith anunchecked
schema.objects()->Get(elem_type->index()).reflection::VerifySchemaBufferperforms structural checks only — it nevervalidates that a
Type.indexis in range againstschema.objects()/schema.enums(). A schema carrying an out-of-range union-member indexverifies cleanly and then drives an out-of-bounds read in
VerifyUnion.GetUnionTypedid avalues()->LookupByKey(tag)and dereferenced the result; for a tag that isnot a declared value this is a null read on a buffer the verifier accepted.
Changes
src/reflection.cppVerifyUnion: resolve the member by value — iterate the enum'svalues()and matchEnumVal.value()to the discriminator, exactly likethe generated per-schema union verifiers (switch over real values). No
matching member value ⇒ reject. Bounds-check
Type.indexagainstobjects().size()before theobjects()->Get(...)dereference.underlying width (
underlying_type()->base_size(): 1/2/4/8 bytes) insteadof a hard-coded
uint8_t, inVerifyObject's union-field handling and inthe union-vector path. Legitimate sparse unions (
A = 555, ...) nowverify; undeclared tags are rejected.
GetSchemaObject/GetSchemaEnum: bounds-checked accessors used bythe verifier's
objects()->Get(...)/enums()->Get(...)sites(
VerifyVectorObj case,VerifyObjectObj case,VerifyUnion).SchemaIsValid: new cross-reference validator — rejects any schemawhose
Type.index(for Obj/Union/Vector-of-Obj/Union types) is out of rangeagainst
objects()/enums(). Called at the entry ofreflection::Verify/VerifySizePrefixed.GetAnyValueS: guard theobjects()->Get(type_index)debug-print path.include/flatbuffers/reflection.hSchemaIsValiddeclaration.GetUnionType: new null-safe overload returningboolwith an outparameter. It validates the enum index, reads the discriminator at the
enum's underlying width, matches by value, and bounds-checks the object
index before dereferencing. The legacy reference-returning overload is kept
for source compatibility (only safe on already-verified buffers).
CopyTable/ResizeTablecall sites updated to the null-safe overload;unresolvable union members are skipped instead of crashing.
src/binary_annotator.{h,cpp}BinaryAnnotator::Annotate()runsSchemaIsValidbefore walking the schema(both constructors).
IsValidUnionValuematches the discriminator by value (not position).IsInlineField,GetElementSize, andBuildUnionbounds-checkType.indexbefore dereferencing and handle unresolvable unions gracefully.Tests
tests/reflection_union_security_test.cpp, registered asReflectionUnionSecurityTest()intests/test.cppand added toFlatBuffers_Tests_SRCSinCMakeLists.txt. It exercises:tag = 555/666(storedint32) now verify; a hand-crafted tag
1(a position but not a declaredvalue) is rejected.
Type.indexis out ofrange is rejected by
reflection::Verify(viaSchemaIsValid) instead ofperforming an out-of-bounds read.
CopyTableon an unresolvable-union buffer nolonger crashes.
Verification
Regression suite (
ReflectionUnionSecurityTest) and the fullflattestsrunwere built and executed locally (Debug + AddressSanitizer); expected output
includes:
Please re-run locally if needed:
Relationship to other PRs
Independent of
harden/deserialize-input-contract, which hardensParser::Deserializeinput handling andType::Deserializerange validation.The two branches share only
src/reflection.cpp/tests/test.cpp/CMakeLists.txtat the file level; they fix distinct root causes and shouldrebase cleanly.
PR body notes for maintainers
attacker-supplied
.bfbsschemas.