Skip to content

scripts: add the check_decode harness the HARDENING corpus runs - #27

Merged
trendvidia merged 2 commits into
mainfrom
fix/map-depth-449
Oct 31, 2026
Merged

trendvidia merged 2 commits into
mainfrom
fix/map-depth-449

Conversation

@bfrancojr

@bfrancojr bfrancojr commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

The HARDENING security corpus can now run against this port. Until now it could not: scripts/check_decode.py does not exist on any branch here, so cross_security_check.sh printed python: no check-decode harness, not run.

Advances trendvidia/protowire#449

All corpus rows pass with no decoder change, on protowire-cpp v1.1.0 or later. That includes the new map-depth rows from protowire PR #465. This PR adds only the harness.

The pin matters

The rows pass only on a build that uses protowire-cpp v1.1.0 or later. This repo's workflows still pin v1.0.0, and on that build 19 of 31 rows pass and one segfaults. #23 has the table, and #26 moves the pin. Until that lands, wheels built from main do not have these limits.

What the rows test

  • PXF and SBE rows go through the wrapped cpp decoder, so any fix for them belongs in protowire-cpp.
  • PB rows go through the stock protobuf runtime (upb here), since this port has no PB decoder. They test what a caller gets from ParseFromString. The limit comes from upb's default.
  • The schema comes from adversarial.binpb, so Tree's branches and tags needed no bindings. I checked the file lists both fields. PB cannot be skipping them as unknown: deep-map-101 and deep-scalar-map-101 fail with Exceeded upb_DecodeOptions_MaxDepth, which needs a descent through those fields.
  • The 22 rows that list python in skip do not run, mostly because they need --limit.

Why this is not the old file

The earlier harness survives only at commit 51df4a6, which is on no branch. I rewrote it because it could pass a row without testing it:

  • SBE was a stub that rejected everything, and all three SBE rows expect a reject. It now goes through sbe.Codec.
  • An uncaught exception exits 1, which the gate reads as a clean reject. Anything other than a decode error now exits 2, which the gate counts as a crash.

Evidence

From the protowire-449 worktree at 819bd78, with protowire-cpp at main (8d0867a):

PYTHON_DIR=<this worktree> SKIP_PORTS=go,cpp,ts,java,rust,swift,dart,csharp bash scripts/cross_security_check.sh
Ran 31 (port, corpus) pairs across 1 ports.
✓ All ports conform to docs/HARDENING.md on the adversarial corpus.

Exit code 0. Before the harness, the same command printed no port has a check-decode binary built and exited 2.

The gate discards each row's output, so I also ran every row by hand and read the reason. All 31 match. The new rows:

pxf/deep-nesting-maps-100.pxf        exit 0
pxf/deep-nesting-maps-101.pxf        exit 1  nesting depth exceeds MaxNestingDepth=100
pxf/deep-nesting-scalar-map-100.pxf  exit 0
pxf/deep-nesting-scalar-map-101.pxf  exit 1  nesting depth exceeds MaxNestingDepth=100
pb/deep-map-100.binpb                exit 0
pb/deep-map-101.binpb                exit 1  Exceeded upb_DecodeOptions_MaxDepth
pb/deep-scalar-map-100.binpb         exit 0
pb/deep-scalar-map-101.binpb         exit 1  Exceeded upb_DecodeOptions_MaxDepth

Failure modes checked: a broken extension build, a missing .binpb and an unknown schema each exit 2.

pytest -q: 100 passed in 0.59s. No test covers the harness itself, and no decoder changed, so there is no fix to remove and re-test.

cross_security_check.sh looks for scripts/check_decode.py and reports the
port as not run without it, so no corpus row has reached this port since the
file was lost. The harness at 51df4a6 is on no branch here.

It is rewritten rather than restored, because that version could pass a row
without testing it: SBE was a stub that rejected everything, and every SBE
row expects a reject; and Python's exit code for an uncaught exception, 1, is
the gate's clean reject. Now SBE goes through the codec, the schema is bound
before the input is read, and anything but a decode error exits 2.

The schema comes from adversarial.binpb, so Tree's new branches and tags
fields (protowire#449) needed no bindings here.
Comment thread scripts/check_decode.py
args = p.parse_args(argv)
try:
return _run(args)
except BaseException as e: # noqa: BLE001 -- exit 1 would read as a clean reject.
@trendvidia
trendvidia merged commit f0db2e4 into main Oct 31, 2026
18 checks passed
@trendvidia
trendvidia deleted the fix/map-depth-449 branch October 31, 2026 08:20
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.

3 participants