11 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) | completed |
|
moderate | medium | component | implementation |
|
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
(
InvalidSpecon pg/mem; the substrate's typed error under the engine's mapping on SQLite) — never a panic, never a silentOkon 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
StringvsInvalidSpecis a known, typed asymmetry) — the differential pins the Ok/Err partition and theOkvalue; - overflow posture: the boundary paths (
skip_forwarddriven with adversarial(next_fire_at, now, interval)triples — thei64::MAXneighborhood, 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 <= 0and giant-narms: leading-zero forms, digit overflow at exactlyi64::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,sharedmanifest gains the three engine crates (path deps — stable-buildable, they already are).- Seeds via
gen_fuzz_seeds.py'severy_parsefunction: every grammar-valid unit form (s|m|h|d), multi-digit and leading-zero forms,@every 0s/@every -5sshapes, thei64::MAX-neighborhood andi64::MAX/86400digit-overflow forms,d-multiplier blowups, whitespace/garbage/truncation shapes, non-grammar prefixes (@once,@hourly-adjacent strings). Corpus committed; gitignore negation. fuzz/README.mdtargets-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.rsexist;cargo fuzz buildbuilds 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.tomlgreen;sharedclippy/fmt gates green- Root workspace gates still green; substrate diff still empty
(amended at implementation, 2026-10-10 — see Notes: the target
found a real no-panic/overflow-posture finding in
skip_forward, dispositioned inline per the task's own finding posture; the substrate delta is two keyword-free lines in the re-derivedqueue_ops.rs, registered as PROVENANCE D-38; lineage body untouched) - 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
@everygrammar 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
Decisions of record the implementation made that the description didn't pin:
- Two real findings, both in
skip_forward(the boundary leg), dispositioned fixed inline (per this task's risk note, the campaign task's posture — small crate-side fixes ride the task that finds them):- Division-by-zero panic + negative-
intervalunder-wrap — SQLite and mem'sskip_forwardcastintervalstraight intoi128(pg hadinterval.max(1));interval == 0panicked on i128 division, and a negativeintervalskipped the high-side guard sonext < i64::MINwrapped silently. Fix: the clamp, pg's committed shape. Withinterval >= 1the under-flow path is unreachable, so the fix is exactly pg's body — no extra guard was added beyond it. - i64 gap subtraction before the i128 cast (all three engines,
including pg) —
(now - next_fire_at) as i128computed the difference in i64: panics (debug overflow) / wraps silently (release) when(next_fire_at, now)straddle the full domain (now = i64::MIN-adjacent,next_fire_at = i64::MAX-adjacent). The 128-bit intermediates story had a hole at its first line. Fix:now as i128 - next_fire_at as i128— same one line, all three. Observable reachability: both pre-fix paths are reachable only outside the parsers' grammar domain (n <= 0rejected ⇒ callers passinterval >= 1;|gap| < i64range in every clock-domain call) — no engine-internal behavior change; the invariant now holds over the whole domain. SQLite's delta registered assubstrate/PROVENANCE.mdD-38 (queue_ops.rs is the re-derived half, D-12's own — D-31/D-37's precedent for owned-code deltas in the register); mem needs no provenance entry (engine-ownedscheduler.rs), and pg's one-line delta is engine-owned likewise.
- Division-by-zero panic + negative-
- (a)-companion leg not built. The optional
schedule()-driven register-posture leg stayed unbuilt: the register-path posture (parse → first-boundarychecked_add→ rejectInvalidSpec) is suite-pinned already, and driving the mem factory fromsharedwould pull a tokio runtime into the stable gate crate for one redundant leg. skip_forwardthree-way equality asserted, not just no-panic. Post-fix the three bodies are lexically identical, so the target goes further than the task text required: three-way output equality on every(next_fire_at, now, interval)triple (fixed adversarial battery + triples folded out of the input bytes at 24-byte stride), plus the saturation pin (i64::MAXclamp or strictly-beyond-now, never a past value).- Error identity left unpinned as specified: SQLite's
Result<i64, String>vs pg/mem'sInvalidSpec— the differential pins partition-and-value; the typed-rejection check (pg/mem must reject withError::InvalidSpec, never another variant) runs ahead of the agreement assert. - Corpus growth noise after the smoke campaign: a 180 s detached
smoke (
run-detached.sh every_parse -max_len=4096 -use_value_profile=1, ~737k execs, 0 crash/oom/timeout — the campaign task re-runs the full budget) grewcorpus/every_parseto 686 files; the grown additions were pruned back to the 66 committed generator seeds since corpus-policy reconciliation (grown-corpus disposition, the seed-promotion posture) is the campaign task's named AC. The!corpus/every_parsenegation does un-ignore grown additions in that dir — the same shape alksocks shipped, so the tension is the campaign task's to resolve, noted here for context.
Summary
Landed the every_parse differential target (2026-10-10): the §3.2
surface — all three one-owner parse_every_interval implementations
driven through the fuzz-parser-exposure #[doc(hidden)] re-exports,
plus all three skip_forward boundary postures.
fuzz/shared/src/every_parse.rs— the invariant logic (stable toolchain, replayable): typed-rejection pin (pg/mem rejects areError::InvalidSpeconly), three-way agreement on partition +Okvalue for arbitrary lossy-decoded strings, a fixed grammar battery (17 accept shapes with pinned interval values incl. leading-zero and thei64::MAX/i64::MAX/86400-neighborhood giant-narms; 27 reject shapes incl. positive-zero, negative, whitespace/truncation, cron/@onceadjacency and unknown units), and the overflow-posture battery overskip_forward(24 fixed adversarial triples + 24-byte-stride triples folded from the raw input; three-way equality + saturate-or-strictly-future pin).#[cfg(test)]corpus replay over the committed dir (≥48 seeds).fuzz/fuzz_targets/every_parse.rs+[[bin]]entry (the payload_codec shape); engine path deps added toshared's manifest; lib.rs module registered.- 66 committed seeds from
gen_fuzz_seeds.py's newevery_parse()(registry wired);!corpus/every_parsegitignore negation;fuzz/README.mdtargets-table row filled. - Two findings found and fixed inline (details in Notes):
skip_forwarddivision-by-zero/negative-intervalwrap (sqlite + mem), and the i64 gap subtraction before the i128 cast (all three engines incl. pg). PROVENANCE D-38 records the substrate delta. - Smoke verdict: 180 s detached campaign, ~737k execs, 0 crash/oom/timeout — the fixes closed out the deterministic finding space; grown corpus pruned to the committed seeds.
Verified: cargo build clean; cargo test workspace green (15 ok
result groups, 0 failures); cargo clippy --all-targets -- -D warnings clean; cargo fmt --check clean; the shared gate
cargo test --manifest-path fuzz/shared/Cargo.toml green;
shared clippy -D warnings + fmt --check green;
cargo fuzz build green from fuzz/ (nightly pin; all three engines
compile under ASAN through the new bin);
cargo check -p alkstore-mem --target wasm32-unknown-unknown clean.
Lineage body untouched; the substrate delta is the two-line
skip_forward fix in the re-derived half, registered as D-38.