Fuzz targets¶
These targets fuzz every untrusted manifest decoder in awc-manifest:
| Target | Decoder under test |
|---|---|
decode_canonical |
awc_manifest::decode (strict canonical CBOR) |
parse_file |
FileManifest::from_canonical_bytes |
parse_tree |
TreeManifest::from_canonical_bytes |
parse_snapshot |
SnapshotManifest::from_canonical_bytes |
The targets assert two invariants:
decode_canonicalrequires byte-exact round-trips: any input the strict CBOR decoder accepts must be a fixed point ofencode ∘ decode, because canonicality is the whole contract at that layer. The decoder also caps container nesting (128 levels); a hostile deeply-nested input is rejected withNestingTooDeepinstead of overflowing the stack.- The three manifest parsers require a stable canonical form: parse the
input, encode it, parse that again, and both the value and its encoding must
be unchanged. The original input need not survive byte-for-byte — unknown
fields are ignored and absent optional fields default, by design (spec §21.8
forward compatibility). What must hold is that one pass through
parse ∘ encodereaches a fixed point with no information the decoder accepted being lost.
Malformed inputs (non-UTF-8 names, empty or oversized objects, wrong kinds via graph, non-contiguous chunks, invalid digests, unknown enum values, runaway nesting) must be rejected rather than panic.
See on-disk format for the canonical encoding these targets exercise.
Seed corpus¶
Each target starts from a committed seed corpus in fuzz/seeds/<target>/. The
seeds are valid manifests and canonical values produced by the crate's own
constructors, plus the structured golden vectors hex-decoded from
tests/manifests/golden_vectors.json. Regenerate them with the stable
toolchain from the repository root:
The generator is deterministic: re-running it must leave git status clean.
The workspace test suite guards the corpus through
crates/awc-manifest/tests/fuzz_seeds.rs, so a seed that stops decoding fails
make test before any fuzz run.
fuzz/corpus/<target>/ is the writable directory libFuzzer fills during a run.
It is git-ignored; fuzz/seeds/ is committed.
Scheduled runs¶
The fuzz workflow
runs every target weekly (Monday 04:17 UTC) and on manual dispatch. The nightly
toolchain is date-pinned, cargo-fuzz is installed from the stable toolchain,
and a dispatch run accepts a max_total_time input (default 300 seconds per
target). On failure the job uploads the crash artifacts and the grown corpus
as a workflow artifact.
Why this is a separate workspace¶
The fuzz crate uses libfuzzer-sys, which requires the nightly toolchain and the
sanitizer runtime. It is its own Cargo workspace (note the [workspace] table in
Cargo.toml) so that cargo run from the repository root — and therefore
make ci — never compiles it. The decoders it targets are otherwise fully
covered by the workspace test suite. See
Run the test suite.
Running¶
cargo +stable install cargo-fuzz --locked # once
cd fuzz
mkdir -p corpus/decode_canonical
cargo +nightly fuzz run decode_canonical corpus/decode_canonical seeds/decode_canonical
libFuzzer writes new inputs only to the first corpus directory, so
fuzz/seeds/ stays untouched. Crash artifacts land under fuzz/artifacts/
(git-ignored). A crash indicates either a non-canonical input that the decoder
wrongly accepted or a panic in the decoding path. Either is a correctness bug.