Repository navigation
producers: refuse a message nested past MaxNestingDepth - #174
Merged
Merged
Conversation
HARDENING.md § Recursion (protowire#502): a producer must fail on a
message nested more deeply than MaxNestingDepth, counted as a decoder of
its output counts it, rather than write what a reader at the same limit
refuses.
Pxf.marshal checks the depth of each '{' and '[' it writes, as
protowire-go's encoder does; an Any body that would go past it falls
back to type_url and value, as it already did at the limit. Pb.marshal
counts submessages and map entries, as Go's pb.MarshalOptions does, so a
reference cycle now meets the limit instead of the stack. Both take the
limit per call.
check-decode's pb-render read its input with protobuf-java's default
recursion limit of 100, so the 101-level rows were refused before the
producer ran. It now reads up to 10000 levels.
This was referenced Nov 3, 2026
Collaborator
Author
|
CI passed on 0e979c9, the PR head read back from the remote: Build + Test, Conformance (Go reference), Analyze and CodeQL. |
trendvidia
pushed a commit
to trendvidia/protowire
that referenced
this pull request
Nov 3, 2026
trendvidia/protowire-java#174 makes Pxf.marshal refuse past MaxNestingDepth and raises check-decode's pb-render reader limit, so the two 101-level pb-render rows test its producer.
trendvidia
pushed a commit
to trendvidia/protowire
that referenced
this pull request
Nov 3, 2026
trendvidia/protowire-java#174 makes Pxf.marshal refuse past MaxNestingDepth and raises check-decode's pb-render reader limit, so the two 101-level pb-render rows test its producer.
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.
Pxf.marshalandPb.marshalnow refuse a message nested more deeply thanMaxNestingDepth(default 100), instead of writing text or bytes that a reader at the same limit refuses. A reference cycle in aPbstruct now gets this error instead of overflowing the stack.Fixes #167
What changes
Pxf.marshalcounts each{and[it writes, the way a decoder of the text counts them. A list of messages is therefore two levels per element, and a map with message values is two per entry. This matches protowire-go's encoder, and the message is Go's:pxf: nesting depth exceeds MaxNestingDepth=100.type_urlandvalue, which any reader can read.Pb.marshalcounts each submessage and map entry, as Go'spb.MarshalOptionsdoes.MarshalOptions.withMaxNestingDepthinpxf, and a neworg.protowire.pb.MarshalOptionswithPb.marshal(Object, MarshalOptions). The old five-argumentMarshalOptionsconstructor still exists, so existing callers compile.check-decode --format pb-renderused protobuf-java's default recursion limit of 100. So the corpus's 101-level rows were refused by the stock reader before the producer ran, and they passed without testing anything. It now reads up to 10000 levels.Classification: reversible by a revert. It adds API (two option methods and one record), which HARDENING.md asks for ("the limit is configurable per call"). Measured. Live: the corpus rows exercise it.
Evidence
./gradlew buildat 0e979c9: 664 tests, 0 failures, 5 skipped. The 5 are pb: null and zero big numbers in Pb.marshal differ from protowire-go, and a null list element throws #159's disabledpbtests, which pb: write null and zero big numbers as protowire-go does, and a BigFloat normalised #170 enables.pxfMarshalDepthTest(5 tests): 100 levels are written and read back, 101 are refused, for submessages, map values and lists. The per-call limit works up and down. An Any body past the limit stays raw.pbMarshalDepthTest(4 tests): submessages, map entries, a cycle, and the per-call limit.deep-submessage-101→reject: Protocol message had too many levels of nesting. That is the stock reader, not the producer.deep-submessage-100exit 0,deep-submessage-101exit 1pxf: nesting depth exceeds MaxNestingDepth=100,deep-map-100exit 0,deep-map-101exit 1 with the same message.skip(corpus: run java on the producer depth rows protowire#538): "Ran 213 (port, corpus) pairs across 3 ports. ✓ All ports conform". That is main's 211 plus the 2 java rows.Merge order
This PR first, then trendvidia/protowire#538. It edits the corpus manifest, and the spec's security workflow builds Java from main.
Not covered here
The lite tier already passes these rows. Its PB producer,
LiteWireWriter, is bounded by the PXF parser's limit. But a well-known-type literal (ts = 2024-01-01T00:00:00Z) is zero PXF levels and one PB level, so a document 100 levels deep with a literal at the bottom writes PB 101 levels deep. That is a question for the spec, not this port, so I filed it as trendvidia/protowire#537.Conflicts
This conflicts lightly with #170, which also edits
Pb.marshalScalar. Whichever merges second needs a rebase. I'll do it.