# alkstore fuzzing libFuzzer targets for alkstore's parse/codec surfaces (see `docs/research/fuzzing.md` for the full rationale and campaign plan). ## Layout - `fuzz_targets/` — one binary per target; thin `fuzz_target!` wrappers (land with the wave-7 target tasks). - `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. It consumes alkstore's public API only and must never become a dependency of any workspace crate. - `corpus//` — committed hand-made seeds (regenerate with `python3 fuzz/gen_fuzz_seeds.py` from the repo root). Grown corpora and artifacts are gitignored; each target task adds its `!`-negation line when its corpus lands. - `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 §5). - `json.dict` — JSON token dictionary for the structured targets. - `gen_fuzz_seeds.py` — deterministic seed generator (quiche pattern). ## Targets | Target | Drives | Invariants | |---|---|---| | `payload_codec` | `encode_payload` / `payload_as::` (`alkstore/src/payload.rs`) | no-panic on any byte sequence; every decode failure typed `Error::Codec`; deep nesting loud at serde_json's recursion limit; `Value` round-trip (serde_json canonicalization pinned); typed round-trip for a representative payload set | | `every_parse` | the three `parse_every_interval` implementations (differential, through the engines' `#[doc(hidden)]` re-exports) + each engine's `skip_forward` boundary arithmetic | no-panic on any UTF-8-lossy string (each parser, each boundary path); typed rejection (`Error::InvalidSpec` on pg/mem, the substrate's error under its mapping on SQLite); three-way agreement on the Ok/Err partition and the `Ok` interval value (error identity not pinned — the SQLite `Result` vs `InvalidSpec` asymmetry is known); overflow posture: `skip_forward` over adversarial `(next_fire_at, now, interval)` triples rejects-or-saturates, never wraps or panics; the `n <= 0` and giant-`n` arms (leading zeros, digit overflow at exactly `i64::MAX/86400`, positive-zero rejection) | | `name_validation` | `validate_name` / `validate_shared_name` / `validate_local_name` (`alkstore/src/validation.rs`) | filled by its target task | ## 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 [extra libfuzzer args...] # corpus replay through the invariants (stable toolchain, no nightly) cargo test --manifest-path fuzz/shared/Cargo.toml # coverage cargo fuzz coverage ``` The fuzz workspace is excluded from the main workspace (`exclude` in the root `Cargo.toml`); it pins its own nightly toolchain via `rust-toolchain.toml` and does not affect the root workspace's stable toolchain or MSRV.