- fuzz/ workspace (nightly-pinned via rust-toolchain.toml, excluded from the main workspace and the published package): chunk_header and envelope_frame targets per fuzzing.md \u00a77.1 - invariant logic in fuzz/shared (stable-toolchain crate): committed corpus replay as plain cargo test (quinn CI pattern, \u00a77.2 tier 3) - envelope target adds FrameError shape-partition asserts, exact consumption accounting, structural write_frame round-trip, serde key-contract, and a trailing-byte probe (prefix counts body only) - chunk_header target adds round-trip identity, TooLarge/short error shape, is_eof, 8-byte consumption, input-never-mutated - committed seed corpora: 245 deterministic seeds via fuzz/gen_fuzz_seeds.py (truncations, len=0/MAX+1/u32::MAX, channel 0, invalid UTF-8, deep nesting); grown corpora + artifacts gitignored - fuzz/run-detached.sh: \u00a77.6 detached runner (setsid+nohup+log, fork mode, rss/malloc limits) \u2014 campaigns never share fate with a session - fuzz/json.dict; README; research doc \u00a77.7 records step-1 status Verification: cargo fuzz build clean; stable side green (cargo test 682 passed, clippy -D warnings, fmt --check incl. fuzz/shared); corpus replay 245 seeds green; detached 10-min campaigns on both targets completed with zero crashes/OOMs/timeouts
53 lines
2.4 KiB
Markdown
53 lines
2.4 KiB
Markdown
# alkcall fuzzing
|
|
|
|
libFuzzer targets for alkcall's wire formats (see
|
|
`docs/research/fuzzing.md` for the full rationale and campaign plan).
|
|
|
|
## Layout
|
|
|
|
- `fuzz_targets/` — one binary per target; thin `fuzz_target!` wrappers.
|
|
- `shared/` — the invariant logic, as a plain library so normal
|
|
`cargo test` (stable toolchain) can replay the committed corpora
|
|
through the same invariants (`fuzz/shared/src/*.rs` `#[cfg(test)]`
|
|
modules; quinn's CI pattern). The fuzz binaries are nightly-only;
|
|
the shared crate is stable-clean.
|
|
- `corpus/<target>/` — committed hand-made seeds (regenerate with
|
|
`python3 fuzz/gen_fuzz_seeds.py` from the repo root). Grown corpora
|
|
and artifacts are gitignored.
|
|
- `run-detached.sh` — mandatory runner for agent sessions: wraps
|
|
`cargo fuzz run` in `setsid` + `nohup` + log redirection so an OOM
|
|
in a target can never take down the agent host (research doc §7.6).
|
|
- `json.dict` — JSON token dictionary for the envelope targets.
|
|
|
|
## Targets
|
|
|
|
| Target | Drives | Invariants |
|
|
|---|---|---|
|
|
| `chunk_header` | `parse_header` / `write_header` (`channels/wire.rs`) | no-panic; round-trip identity; `TooLarge` iff `length > MAX_CHUNK_LEN`; consumption accounting (8 bytes); input never mutated |
|
|
| `envelope_frame` | `FrameFramedReader::new(Cursor).read_frame()` on a current-thread runtime | no-panic; never allocates > `MAX_FRAME_SIZE`; clean `FrameError` on truncation/oversize/zero-length; exact consumption; structural `write_frame` round-trip |
|
|
|
|
## Commands
|
|
|
|
`cargo fuzz build` and `cargo fuzz run` must be executed with `fuzz/`
|
|
(or deeper) as the working directory so rustup selects the pinned
|
|
nightly toolchain — the detached runner handles that itself.
|
|
|
|
```sh
|
|
# build (nightly, pinned by rust-toolchain.toml inside fuzz/; run from fuzz/)
|
|
cargo fuzz build
|
|
|
|
# agent sessions: detached campaign (never foreground; CWD-independent)
|
|
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh chunk_header
|
|
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh envelope_frame -max_len=65536 -dict=json.dict
|
|
|
|
# corpus replay through the invariants (stable toolchain, no nightly)
|
|
cargo test --manifest-path fuzz/shared/Cargo.toml
|
|
|
|
# coverage
|
|
cargo fuzz coverage <target>
|
|
```
|
|
|
|
The fuzz workspace is excluded from the main workspace and from the
|
|
published package (`exclude` in the root `Cargo.toml`); it pins its own
|
|
nightly toolchain via `rust-toolchain.toml` and does not affect the
|
|
crate's stable MSRV. |