Skip to content

[C#] Verify the file identifier instead of skipping the check - #9211

Open
Nexory wants to merge 1 commit into
google:masterfrom
Nexory:csharp-verify-identifier
Open

[C#] Verify the file identifier instead of skipping the check#9211
Nexory wants to merge 1 commit into
google:masterfrom
Nexory:csharp-verify-identifier

Conversation

@Nexory

@Nexory Nexory commented Aug 30, 2026

Copy link
Copy Markdown

Verifier.VerifyBuffer(identifier, ...) never compares the file identifier, and
for a schema without a file_identifier it throws instead of verifying.

CheckBufferFromStart gates the comparison on identifier.Length == 0.
VerifyBuffer rejects an empty identifier earlier with ArgumentException, so
the guarded branch is unreachable and both cases below fall through to "valid".

The contract documented further down in the same file says the opposite:
"identifier - the expected identifier of buffer data. When empty identifier is
provided the identifier validation is skipped."

C++ (verifier.h) and Swift (Root.swift) both check.

Measured on master 5761d6e, before the change:

call expected actual
buffer "NULL", verified against "ZZZZ" false true
buffer without identifier, verified against "NULL" false true
VerifyBuffer("") on a valid buffer true (skipped) ArgumentException: file identifier must be length4

The last row is what generated code hits: flatc emits VerifyBuffer("", ...) for
schemas that declare no file_identifier, for example
tests/type_field_collsion/Collision.cs and
goldens/csharp/flatbuffers/goldens/Universe.cs. That is issue #8002, which the
stale bot closed without an answer.

The fix flips the condition to identifier.Length != 0.

One existing test changes with it. TestVerifyingUnions finished its buffer with
fbb.Finish(monster_outer.Value), leaving out the "MONS" identifier, and then
asserted that Monster.VerifyMonster(...) returns true. That assertion held only
because the check never ran, so it now uses Monster.FinishMonsterBuffer.

This is a behavior change worth calling out: code that verifies a buffer against
an identifier the buffer does not carry now gets false where it got true before.
That is the documented contract, but it can surface in existing callers.

Tests: 4 cases covering a matching, mismatched, missing and empty identifier.
With the fix the suite reports 160 tests run, 0 failed; without it 3 of the 4
fail. On master without the new file it reports 156 tests run, 0 failed, so
nothing else changes. I ran the build and test matrix from the workflow (default,
UnsafeByteBuffer, EnableSpanT plus UnsafeByteBuffer) for net8.0 and net6.0.

I left Rust alone: its verifier takes no identifier parameter at all, which is a
separate gap rather than the same bug.

CheckBufferFromStart() gated the identifier comparison on
identifier.Length == 0, so the comparison could only run when the caller
passed an empty identifier. VerifyBuffer() rejects an empty identifier
earlier with ArgumentException, so the branch was unreachable: a buffer
carrying a wrong identifier, or none at all, verified as valid.

The contract documented in the same file states the opposite: "When empty
identifier is provided the identifier validation is skipped." The C++
verifier (verifier.h) and Swift (Root.swift) both check. flatc emits
VerifyBuffer("", ...) for schemas that declare no file_identifier, which
today throws instead of skipping.

Flip the condition to identifier.Length != 0 and cover matching,
mismatched, missing and empty identifiers with tests.

TestVerifyingUnions finished its buffer with fbb.Finish(root), omitting
the "MONS" identifier, while asserting that Monster.VerifyMonster()
returns true. That assertion held only because the check never ran, so it
now uses Monster.FinishMonsterBuffer().
@Nexory
Nexory requested a review from dbaileychess as a code owner August 30, 2026 03:00
@github-actions github-actions Bot added the c# label Aug 30, 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.

1 participant