Files
alkstore/tasks/fuzz-target-payload-codec.md
T

3.6 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
fuzz-target-payload-codec Fuzz target — `payload_codec` (core payload encode/decode invariants) pending
fuzz-scaffold
moderate low component implementation
wave-7
fuzzing

Description

The §3.1 target: payload_codec, driving core's payload surface (alkstore/src/payload.rs — encode_payload, payload_as::<T>) — already-pub API, no exposure needed.

  • fuzz_targets/payload_codec.rs — the thin fuzz_target! wrapper (3-line pattern) delegating to alkstore_fuzz_shared::payload_codec::fuzz_payload_codec(data); the [[bin]] entry lands in fuzz/Cargo.toml.
  • shared/src/payload_codec.rs — the invariant logic, with the #[cfg(test)] corpus-replay module (the gate). Input: raw &[u8] plus arbitrary-derived serde_json::Values (the JsonValue bounded-depth helper from the scaffold). Invariants (§3.1, verbatim):
    • no-panic on any byte sequence (the baseline);
    • typed-failure partition: every decode failure is Error::Codec — no other variant, no panic (the ADR-023 §1 fallibility pin, demonstrated across the input space);
    • deep-input loudness: serde_json's recursion limit surfaces as typed Codec, never a stack overflow;
    • Value round-trip: encode_payload(v) → payload_as::<Value> yields v for every arbitrary Value (canonicalization is serde_json's, pinned-not-regressed);
    • typed round-trip: for a representative typed-payload set, encode(Value::from(t)) decodes back to t (the ADR-020 §4 decode side).
  • Seeds (§3.1's list, via gen_fuzz_seeds.py gaining the payload_codec function): valid JSON of every Value variant, truncations, deep nesting at and past the recursion limit, non-UTF-8 bytes, trailing garbage, oversized numbers, duplicate keys, the Null-decode shape (ADR-023 §1's empty-byte-store corruption note). Corpus committed under fuzz/corpus/payload_codec/; the fuzz/.gitignore gains the !corpus/payload_codec negation.
  • fuzz/README.md targets-table row.
  • shared's manifest gains alkstore (path) if the scaffold didn't already carry it, plus serde_json + arbitrary.

Campaign flags are the campaign task's concern (-max_len=65536 -dict=json.dict starting point, §7); this task lands target + seeds + replay.

Acceptance Criteria

  • fuzz_targets/payload_codec.rs + shared/src/payload_codec.rs exist; cargo fuzz build builds the target (nightly, ASAN)
  • All five §3.1 invariants implemented in shared and exercised by both the fuzz path and the #[cfg(test)] replay module
  • Seed corpus committed (fuzz/corpus/payload_codec/), generated by python3 fuzz/gen_fuzz_seeds.py (deterministic — re-running is a no-op diff); gitignore negation in place
  • cargo test --manifest-path fuzz/shared/Cargo.toml green (replays the committed corpus through the invariants)
  • shared clippy/fmt gates green (cargo clippy --manifest-path fuzz/shared/Cargo.toml --all-targets -- -D warnings, fmt --check)
  • Root workspace gates still green

References

  • docs/research/fuzzing.md §3.1 (target spec), §4 (wave-1 scope), §7 (flags)
  • docs/architecture/decisions/020-payload-encoding.md §4 (the encoding posture the round-trips pin)
  • docs/architecture/decisions/023-fourth-review-round.md §1 (encode fallibility)
  • /workspace/@alkdev/alksocks/fuzz/shared/src/datagram_codec.rs (the invariant-module + replay-test pattern)

Notes

To be filled by implementation agent

Summary

To be filled on completion