Skip to content

Throw instead of aborting on wire type mismatches in the Swift runtime - #3731

Merged
oldergod merged 1 commit into
masterfrom
bquenaudon.2026-09-25.proto-reader-throw-on-wire-type-mismatch
Sep 25, 2026
Merged

oldergod merged 1 commit into
masterfrom
bquenaudon.2026-09-25.proto-reader-throw-on-wire-type-mismatch

Conversation

@oldergod

Copy link
Copy Markdown
Member

Harden the Swift decoder so that bad bytes throw an error and do not stop the process.

ProtoReader.readBuffer, readData, readFixed32, readFixed64 and readVarint check that the wire type of the current field is the one they read. When the check failed, they called fatalError or precondition. Generated code picks the read primitive from the schema, but the wire type comes from the input. So a known field number with a different wire type stopped the process, in debug and in release builds. A do/catch around ProtoDecoder.decode cannot handle this.

The five functions already throw, so no caller changes. On a mismatch they now throw ProtoDecoder.Error.invalidStructure(message:). This is the same approach as beginMessage, which throws messageWithoutLength for its own state check, and as the Kotlin runtime, which throws ProtocolException. I did not use invalidFieldWireType, because that case reports a wire type value that does not exist.

Valid input, truncated input and unknown fields decode the same as before.

Tests:

  • ProtoReaderTests: one test for each of the five primitives. Each test gives a field key with a different wire type and expects invalidStructure with the message.
  • ProtoDecoderTests.testDecodeRejectsKnownFieldWithMismatchedWireType: decodes Person with known fields sent with each wrong wire type, and expects a thrown error.

Without the fix, each new test stops the test process with signal 5 at its own check.

ProtoReader.readBuffer, readData, readFixed32, readFixed64 and readVarint
checked the wire type of the current field with fatalError or
precondition. Generated code picks the read primitive from the schema, but
the wire type comes from the input. A known field number sent with a
different wire type therefore stopped the process. A catch block cannot
handle a failed precondition, so a caller had no way to reject the bytes.

The five functions already throw. They now throw
ProtoDecoder.Error.invalidStructure on a mismatch, as beginMessage does for
its own state check and as the Kotlin runtime does with ProtocolException.
Valid input and unknown fields decode the same as before.
@oldergod
oldergod marked this pull request as ready for review September 25, 2026 15:48
@oldergod
oldergod requested a review from dnkoutso September 25, 2026 15:48
@oldergod
oldergod merged commit 36f883b into master Sep 25, 2026
17 checks passed
@oldergod
oldergod deleted the bquenaudon.2026-09-25.proto-reader-throw-on-wire-type-mismatch branch September 25, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants