Files
irtt-rs/fuzz/README.md
T

81 lines
3.4 KiB
Markdown

# `irtt-rs` fuzzing
Coverage-guided fuzzing of `irtt-proto` wire decoders and `irtt-server`'s public
`ServerCore::handle_datagram` API via
[`cargo-fuzz`](https://github.com/rust-fuzz/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` |
| `server_core_datagrams` | A bounded mix of raw datagrams and production-encoded Open/Echo/Close requests against one `ServerCore`; it varies IPv4/IPv6 peers and session/HMAC configuration, authenticates keyed requests, reuses issued tokens, and asserts the session cap |
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 (`MAX_INPUT_LEN` in each harness) so libFuzzer
spends its time exploring protocol structure rather than allocating/copying
megabytes. `server_core_datagrams` additionally caps its sequence length and
each individual datagram and configures the core to the same packet cap, so one
libFuzzer input cannot become a tiny DoS benchmark.
## Running
```sh
cargo +nightly fuzz run decode_request
```
Bounded smoke run, e.g. 30 seconds:
```sh
cargo +nightly fuzz run decode_request -- -max_total_time=30
```
Build every target without running:
```sh
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:
```sh
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`.