Files
alkstore/tasks/fuzz-target-every-parse.md
T

4.7 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
fuzz-target-every-parse Fuzz target — `every_parse` (the three `@every` parsers, differential) pending
fuzz-scaffold
fuzz-parser-exposure
moderate medium component implementation
wave-7
fuzzing

Description

The §3.2 target: every_parse — the wave's load-bearing target, since the crate's own track record (the wave-6 gate's three edge-domain findings) lives exactly here. Drives the three per-engine parse_every_interval implementations differentially through the fuzz-parser-exposure surfaces, plus each engine's next-boundary arithmetic (skip_forward):

  • SQLite: alkstore-sqlite's re-export (substrate-owned, Result<i64, String>),
  • Postgres: alkstore-postgres's re-export (Result<i64> → InvalidSpec),
  • Mem: alkstore-mem's re-export (Result<i64> → InvalidSpec).

Input: arbitrary &[u8] → UTF-8-lossy Strings (lossy is deliberate — the engines receive &str, always valid UTF-8; the generator still stresses the grammar's edge shapes). Invariants (§3.2, verbatim):

  • no-panic on any string (each parser, each boundary path);
  • typed rejection: every rejection is the parser's own typed error (InvalidSpec on pg/mem; the substrate's typed error under the engine's mapping on SQLite) — never a panic, never a silent Ok on a grammar-violating shape the authored tests already reject;
  • three-way agreement: the accept-set and the interval value are identical across the three parsers for every input. Error identity is NOT pinned equal (the substrate's String vs InvalidSpec is a known, typed asymmetry) — the differential pins the Ok/Err partition and the Ok value;
  • overflow posture: the boundary paths (skip_forward driven with adversarial (next_fire_at, now, interval) triples — the i64::MAX neighborhood, negative shapes, d-unit multiplier blowups) reject-or-saturate per each engine's pinned semantics, never wrap or panic — the wave-6 scheduler-register fix's invariant over the whole domain;
  • the n <= 0 and giant-n arms: leading-zero forms, digit overflow at exactly i64::MAX/86400, positive-zero rejection.

The register-path posture (parse → first-boundary checked_add → reject with InvalidSpec, the wave-6 alignment) is suite-pinned already; the target's optional (a)-companion leg may drive the public schedule() on the mem/SQLite factories for register-path seeds — optional, recorded if used.

  • fuzz_targets/every_parse.rs + shared/src/every_parse.rs (invariants + #[cfg(test)] replay), [[bin]] entry, shared manifest gains the three engine crates (path deps — stable-buildable, they already are).
  • Seeds via gen_fuzz_seeds.py's every_parse function: every grammar-valid unit form (s|m|h|d), multi-digit and leading-zero forms, @every 0s / @every -5s shapes, the i64::MAX-neighborhood and i64::MAX/86400 digit-overflow forms, d-multiplier blowups, whitespace/garbage/truncation shapes, non-grammar prefixes (@once, @hourly-adjacent strings). Corpus committed; gitignore negation.
  • fuzz/README.md targets-table row.

Risk note: medium because the differential may surface a real three-way divergence (that is the target's purpose) — a finding is success, dispositioned per the campaign task's posture (fix inline or escalate), not a task failure.

Acceptance Criteria

  • fuzz_targets/every_parse.rs + shared/src/every_parse.rs exist; cargo fuzz build builds the target
  • All five §3.2 invariant classes implemented and exercised by both the fuzz path and the replay module; the three-way agreement asserted on every input (fuzz) and every seed (replay)
  • Seed corpus committed, deterministic-generated, gitignore negation in place
  • cargo test --manifest-path fuzz/shared/Cargo.toml green; shared clippy/fmt gates green
  • Root workspace gates still green; substrate diff still empty
  • Any three-way divergence found during target development is recorded in Notes with its disposition (fixed / escalated), not silently absorbed

References

  • docs/research/fuzzing.md §3.2 (target spec + the exposure decision this target consumes), §4, §7
  • docs/architecture/decisions/009-queue-and-scheduler.md §2 (the @every grammar the three parsers implement)
  • docs/architecture/decisions/012-forked-substrate-design.md §2 (one-owner rule — why there are three parsers to differ)
  • tasks/review-wave-6.md (the wave-6 finding class this target generalizes; the register-overflow alignment)
  • tasks/fuzz-parser-exposure.md (the surfaces this target drives)

Notes

To be filled by implementation agent

Summary

To be filled on completion