Files
irtt-rs/fuzz
Nils Andreas Svee 719073df1c test(proto): add decoder fuzzing
Add isolated cargo-fuzz/libFuzzer infrastructure for irtt-proto, fuzzing
decode_request, decode_open_reply, decode_echo_reply, Params::decode(_with_presence),
and the varint codec against arbitrary and structurally-derived wire input.
Each target's core invariant is that arbitrary bytes must never panic; a
small clean-room seed corpus (generated from irtt-proto's own encoders) and
a bounded nightly smoke CI workflow are included. The fuzz project is its
own isolated cargo workspace and does not affect the root workspace, its
lockfile, or any published crate's dependency graph.
2026-08-19 23:33:27 +07:00
..
2026-08-19 23:33:27 +07:00
2026-08-19 23:33:27 +07:00
2026-08-19 23:33:27 +07:00
2026-08-19 23:33:27 +07:00
2026-08-19 23:33:27 +07:00

irtt-proto fuzzing

Coverage-guided fuzzing of irtt-proto's wire decoders via cargo-fuzz/libFuzzer.

This project is intentionally isolated from the normal stable/MSRV workspace: it lives in its own [workspace] under fuzz/, is never a member of the root workspace, requires nightly Rust, and is not built by cargo check/test/clippy/doc at the workspace root.

Prerequisites

  • nightly Rust (rustup toolchain install nightly)
  • cargo-fuzz (cargo install cargo-fuzz --locked --version 0.13.1)

Targets

Target Exercises
decode_request decode_request, Params::decode on a decoded Open, verify_packet_hmac
decode_open_reply decode_open_reply, unauthenticated and HMAC-authenticated
decode_echo_reply decode_echo_reply against a deterministic, bounded Params matrix, unauthenticated and HMAC-authenticated
decode_params Params::decode and Params::decode_with_presence, plus an encode/decode round trip
decode_varint varint::decode_uvarint/decode_varint and their round trip through encode_uvarint/encode_varint

The invariant every target asserts is: arbitrary bytes must never panic. Returning Err is normal and expected; a successful decode is also normal.

Every target bounds its input to 128 KiB (MAX_INPUT_LEN in each harness) so libFuzzer spends its time exploring protocol structure rather than allocating/copying megabytes — production IRTT datagrams are UDP-sized, well under this bound.

Running

cargo +nightly fuzz run decode_request

Bounded smoke run, e.g. 30 seconds:

cargo +nightly fuzz run decode_request -- -max_total_time=30

Build every target without running:

cargo +nightly fuzz build

Corpus

Seed corpora live under fuzz/corpus/<target>/. They were generated once from irtt-proto's own public encoders (encode_request, encode_open_reply, encode_echo_reply, Params::encode) by a throwaway local script, not copied from any external source, and not derived from upstream heistp/irtt. Each file is a small, representative wire packet (minimal Open, Close, Echo, HMAC variants, ordinary/recv-count/recv-window/midpoint Echo replies, and a few Params payloads including an unknown tag and server_fill).

Crash artifacts

A failing case is written under fuzz/artifacts/<target>/. To replay it:

cargo +nightly fuzz run decode_request fuzz/artifacts/decode_request/<crash-file>

Isolation notes

  • fuzz/Cargo.toml declares its own [workspace], so it is never picked up by the root [workspace] in ../Cargo.toml.
  • libfuzzer-sys and arbitrary are fuzz-only dependencies; they never appear in any published crate's dependency graph.
  • fuzz/Cargo.lock is committed for reproducible fuzzing builds, independent of the root Cargo.lock.