Skip to content

test(protocol): adversarial stream corpus for parser robustness (#1195)#4181

Merged
oferchen merged 4 commits into
masterfrom
test/protocol-stream-corpus-1195
May 16, 2026
Merged

test(protocol): adversarial stream corpus for parser robustness (#1195)#4181
oferchen merged 4 commits into
masterfrom
test/protocol-stream-corpus-1195

Conversation

@oferchen
Copy link
Copy Markdown
Owner

Summary

  • Add crates/protocol/tests/adversarial_stream_corpus.rs, a curated catalogue of malformed, truncated, and edge-case byte streams that exercise the multiplex reader, legacy @RSYNCD: handshake parser, varint and fixed-int decoders, delta token decoder, file-list name decoder, and filter-rule wire decoder.
  • Each corpus entry pins an expected outcome (Ok, specific io::ErrorKind, or NoPanic) and carries an inline comment describing the regression it would catch.
  • Three reusable runners assert outcomes against the recorded expectations and run every case through catch_unwind to guarantee parsers never panic, crash, OOM, or hang.
  • Designed as the static regression seed for live interop fuzzing (Prevent module burst from raising daemon bwlimit #1196). New entries are added by appending to the relevant static array.

Coverage

  • Truncated multiplex headers (1- and 3-byte) and oversized 16 MiB length claims with no payload.
  • Tags below MPLEX_BASE and unmapped message codes.
  • Multiplex header bytes embedded inside another header's payload (asserts no recursive descent).
  • Invalid UTF-8 inside MSG_INFO payloads (multiplex layer must remain transparent).
  • Zero-length MSG_DATA frames (valid flush boundary).
  • Truncated legacy @RSYNCD: greetings, malformed subprotocol markers, and saturating numeric overflow.
  • Negotiation prologue detector replayed for every truncation of a full legacy banner.
  • Delta token decoder: empty stream, truncated read_int, end marker, negative block match, i32::MIN block match.
  • Internal delta opcode decoder: negative literal length rejection.
  • Varint decoder: overflow leading byte, empty stream, truncated 4-byte extension.
  • Legacy 4-byte read_int and longint extension truncation.
  • File-list name decoder: embedded NUL bytes preserved, truncated long-name suffix surfaces EOF.
  • Filter list wire decoder: negative length, oversized length, invalid UTF-8 body, terminator-only stream, embedded NUL in pattern.

Test plan

  • cargo nextest run -p protocol --all-features -E 'test(adversarial_stream_corpus)' passes on Linux, macOS, and Windows.
  • Full cargo nextest run --workspace --all-features remains green.

@oferchen oferchen force-pushed the test/protocol-stream-corpus-1195 branch from b7c4090 to 4d1d2b2 Compare May 16, 2026 20:44
@github-actions github-actions Bot added the test label May 16, 2026
@oferchen oferchen force-pushed the test/protocol-stream-corpus-1195 branch 3 times, most recently from 7b5ba4b to 0192dda Compare May 16, 2026 22:38
oferchen added 4 commits May 17, 2026 02:20
Add a curated corpus of malformed, truncated, and edge-case byte streams
that exercise the multiplex reader, legacy `@RSYNCD:` handshake parser,
varint and fixed-int decoders, delta token decoder, file-list name
decoder, and filter-rule wire decoder. Each entry carries an inline
comment describing the regression it would catch, and three runners
assert documented outcomes: clean parse, specific error kind, or no
panic under `catch_unwind`.

Designed as the static regression seed for live interop fuzzing (#1196).
The corpus is extensible: new adversarial inputs are added by appending
to the relevant static array.
Adds one MULTIPLEX_CASES entry tagged Outcome::NoPanic (arbitrary bytes
that must not crash the reader) and one LEGACY_GREETING_CASES entry
tagged GreetingOutcome::Ok (canonical \@rsyncd: 32.0 line).
Removes the dead_code warning the unused variants triggered without
silencing the lint.
@oferchen oferchen force-pushed the test/protocol-stream-corpus-1195 branch from 0192dda to 677b5f4 Compare May 16, 2026 23:20
@oferchen oferchen merged commit d2bfbfe into master May 16, 2026
12 checks passed
@oferchen oferchen deleted the test/protocol-stream-corpus-1195 branch May 16, 2026 23:20
oferchen added a commit that referenced this pull request May 18, 2026
… (#4181)

* test(protocol): adversarial stream corpus for parser robustness (#1195)

Add a curated corpus of malformed, truncated, and edge-case byte streams
that exercise the multiplex reader, legacy `@RSYNCD:` handshake parser,
varint and fixed-int decoders, delta token decoder, file-list name
decoder, and filter-rule wire decoder. Each entry carries an inline
comment describing the regression it would catch, and three runners
assert documented outcomes: clean parse, specific error kind, or no
panic under `catch_unwind`.

Designed as the static regression seed for live interop fuzzing (#1196).
The corpus is extensible: new adversarial inputs are added by appending
to the relevant static array.

* style(protocol): apply rustfmt to adversarial_stream_corpus

* fix(protocol): move cursor inside catch_unwind closure for UnwindSafe

* test(protocol): add corpus entries that exercise NoPanic and Ok outcomes

Adds one MULTIPLEX_CASES entry tagged Outcome::NoPanic (arbitrary bytes
that must not crash the reader) and one LEGACY_GREETING_CASES entry
tagged GreetingOutcome::Ok (canonical \@rsyncd: 32.0 line).
Removes the dead_code warning the unused variants triggered without
silencing the lint.
oferchen added a commit that referenced this pull request May 18, 2026
… (#4181)

* test(protocol): adversarial stream corpus for parser robustness (#1195)

Add a curated corpus of malformed, truncated, and edge-case byte streams
that exercise the multiplex reader, legacy `@RSYNCD:` handshake parser,
varint and fixed-int decoders, delta token decoder, file-list name
decoder, and filter-rule wire decoder. Each entry carries an inline
comment describing the regression it would catch, and three runners
assert documented outcomes: clean parse, specific error kind, or no
panic under `catch_unwind`.

Designed as the static regression seed for live interop fuzzing (#1196).
The corpus is extensible: new adversarial inputs are added by appending
to the relevant static array.

* style(protocol): apply rustfmt to adversarial_stream_corpus

* fix(protocol): move cursor inside catch_unwind closure for UnwindSafe

* test(protocol): add corpus entries that exercise NoPanic and Ok outcomes

Adds one MULTIPLEX_CASES entry tagged Outcome::NoPanic (arbitrary bytes
that must not crash the reader) and one LEGACY_GREETING_CASES entry
tagged GreetingOutcome::Ok (canonical \@rsyncd: 32.0 line).
Removes the dead_code warning the unused variants triggered without
silencing the lint.
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