From f809f6ca7eff262e5186c26614fbe509cb05ca5c Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Wed, 30 Sep 2026 08:55:30 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20fuzzing=20wave-3=20status=20=E2=80=94?= =?UTF-8?q?=20release-budget=20campaigns,=20W3-1/2/3=20findings,=20all=20f?= =?UTF-8?q?ive=20targets=20in-tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §Status now covers waves 1-3. §5 gains the release-budget numbers (bast_compile 818k execs still growing; data_access 11.3M execs with value-profile lifting the saturated edge count 379 -> 684; read_opseq/layout_build 63k execs each with the stateful search active through budget; validate_pair 37k execs clean after the fixes). §3 target-5 marked implemented; §4 records the 48-seed menu; §6 gains candidate 8 (the W3-3 aligned maxLength reservation bug, confirmed+fixed) and rewrites the evidence summary; §7 wave 3 is checked off — corpus replay 30/30 stands as the gate. Verification: cargo doc clean; 573 main-crate tests + 30 replay tests green; clippy -D warnings clean (crate + shared). --- docs/plans/fuzzing.md | 151 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 18 deletions(-) diff --git a/docs/plans/fuzzing.md b/docs/plans/fuzzing.md index 055db60..a11687c 100644 --- a/docs/plans/fuzzing.md +++ b/docs/plans/fuzzing.md @@ -22,16 +22,71 @@ pairs) — exactly the shapes where example tests miss off-by-one bugs. The crate is fully synchronous, so targets are simpler than alkcall/alkhttp's (no current-thread runtime shims anywhere). -**Status:** waves 1–2 implemented and verified (2026-09-30). The +**Status:** waves 1–3 implemented and verified (2026-09-30). The pre-fuzzing inventory (§6) is verified against the code at 0.3.0. -Targets 1–4 and the full infrastructure are in-tree (commits `ed41d77`, -wave 1; wave 2); wave-1 smoke campaigns ran clean (§5); the wave-2 -`read_opseq` campaign surfaced one real packing bug on its first -semantic fixture — the fixed-stride array window bounds check -(§6 candidate 6) — fixed same-day with a regression test. +All five targets and the full infrastructure are in-tree (commits +`ed41d77`, wave 1; `16b9023`, wave 2; `aef8d9f` + findings commits, +wave 3); the smoke campaigns ran clean (§5); the release-budget +campaigns across all five targets are complete (§5) — two targets +surfaced real findings: the wave-2 `read_opseq` packing bug (§6 +candidate 6, fixed same-day) and three wave-3 `validate_pair` +findings (W3-1 harness pin, W3-2 upstream serde_json pin, W3-3 a real +engine bug — read_field/write_field misread aligned maxLength +reservations — fixed same-day with regression tests). Progress log: +- **2026-09-30 — wave 3 landed.** Target 5 (`validate_pair`): the + two-input harness (10-lane schema menu incl. a raw-JSON-bytes lane + fused with the buffer), 48 committed seeds, decode-pin tests, and + the §3 target-5 invariants (mode agreement incl. the documented + aligned-rejection taxonomy, the materialize⇄validate_bytes verdict + lattice with verbatim error propagation, unknown-path echo, + non-finite-float Access pin, enum-Validation pin, record spin + bound). Pre-campaign hand-drives all held. **Release-budget + campaigns (§5):** 45–46 min across all five targets. bast_compile + 818k execs / 19,894 edges (still growing at budget end); + data_access 11.3M execs / 684 edges (a value-profile run lifted the + saturated corpus from 379 to 684 edges); read_opseq 63k execs / + 17,519 edges; layout_build 63k execs / 17,659 edges; validate_pair + 37k execs / 10,850 edges. **Finding W3-1 (harness invariant + corrected — the fuzzer fired an over-assertion):** the harness + claimed validate_bytes Ok ⇒ every offset-map leaf's range.end ≤ + buffer.len() — false for offset-indirect entries, whose pair points + absolutely into the buffer while a maxLength window may dwarf the + validated buffer; the wave-1 data_access bounds partition is the + real contract. Fixed the invariant, pinned the artifact bytes as + seed-044 + a named regression test (commit `9ca9922`). + **Finding W3-2 (upstream, pinned with slack — no alktype bug):** + serde_json's non-`float_roundtrip` parser drifts one ulp when + re-parsing its own emitted shortest repr of adversarial f64 values + (probe: 0x5bffffffffffffff emits 1.4536774485912136e+135 and + parses back one ulp low; std's parser and ryu's own float parse are + exact — the concise reparse is the drift). The harness replaced + bare `Value` equality with a one-ulp structural comparison; the + artifact bytes are seed-047 (commits `a8e955c`/`b7ead99`). + **Finding W3-3 (real engine bug — fixed, commit `a0dd3d2`):** + `AlkTypeEngine::read_field`/`write_field` treated an aligned + `maxLength` reservation (ADR-003 strategy 2, `VARCHAR(N)`: raw + zero-padded window, NUL-trimmed on read — exactly what the + materializer and `validate_bytes` implement) as length-prefixed, + parsing the window's first four raw bytes as a u32 length. Every + aligned schema declaring `maxLength` broke the + validate_bytes⇒read_field lattice whenever the reservation's first + bytes looked like a large prefix (validate Ok, read_field Access + with bogus bounds; write_field wrote prefix+data into a raw + window). Third crash artifact (W3-1's shape family: the campaign + re-found the disagreement space after W3-1's invariant was + corrected). Fix: `VariableEncoding` gains `MaxLengthReserved` + (additive variant, ADR-003 strategy 2); `OffsetMap::compute` + records it for maxLength fields with the default encoding + (`maxLength`+`offset-indirect` stays `OffsetIndirect`, preserving + W3-1's combination semantics); `read_field`/`write_field` dispatch + through new `data_access::read_reservation{,_string}/ + write_reservation` (single source of truth with the materializer); + three engine regression tests + the W3-1/W3-2 artifacts as + committed corpus seeds. Post-fix restart: 30-min validate_pair + campaign clean to budget end (37k execs, 10,850 edges, exit 0). - **2026-09-30 — wave 2 landed.** Targets 3 (`read_opseq`) and 4 (`layout_build`) with 73 committed seeds (58 + 15), decode-pin tests for the arbitrary 1.4.2 derive encoding, and the plan §6 candidate-1 @@ -172,7 +227,7 @@ checklist, as the siblings did. | 2 | `data_access` | the hand-rolled decode core (`src/data_access.rs`, read + write side) | raw `&[u8]` + chosen (offset, endian) | implemented 2026-09-30 | | 3 | `read_opseq` | stateful `SequentialReader` op sequences over hostile bytes under a fixed plan | `#[derive(Arbitrary)]` op enum | implemented 2026-09-30 | | 4 | `layout_build` | `LayoutBuilder::build` with adversarial `var_sizes` | `#[derive(Arbitrary)]` map shapes | implemented 2026-09-30 | -| 5 | `validate_pair` | two-input structured: compile a schema once per exec, hammer hostile bytes through `validate_bytes`/`read_field`/`materialize` | `#[derive(Arbitrary)]` (doc, bytes) pair | planned (wave 3) | +| 5 | `validate_pair` | two-input structured: compile a schema once per exec, hammer hostile bytes through `validate_bytes`/`read_field`/`materialize` | `#[derive(Arbitrary)]` (doc, bytes) pair | implemented 2026-09-30 | ### Target 1 — `bast_compile` (the whole schema side, one choke point) @@ -355,9 +410,11 @@ grown corpora and artifacts gitignored. Seed menus: 1.4.2 derive layout and *pinned by decode tests* (both targets carry a `decode_lands_on_the_intended_variants` replay test, the alkhttp target-4 pattern). -- `validate_pair` (wave 3): hostile-schema/valid-bytes, - valid-schema/hostile-bytes, valid/valid — plus the §6 candidate - shapes as pinned reproducers. +- `validate_pair` (wave 3, **done 2026-09-30**): hostile-schema/ + valid-bytes, valid-schema/hostile-bytes, valid/valid — plus the §6 + candidate shapes as pinned reproducers. 48 committed seeds incl. + the W3-1/W3-2 artifact bytes, the aligned maxLength fixtures, and + the mode-agreement pins (ADR-006/ADR-008 lanes). Stateful `Arbitrary` seeds are hand-encoded against the `arbitrary` 1.4.x derive layout with per-element keep-going bytes, pinned by @@ -386,6 +443,34 @@ repo): MSRV, and wasm target are untouched — `cargo fuzz build` must never be a prerequisite for `cargo test`/`clippy`/`build`. +Release-budget campaign results (2026-09-30, 45–46 min per target, +§5 policy, detached, `-use_value_profile=1`, `-dict=json.dict` on the +JSON lanes): all five exited 0. + +- `bast_compile` — 818k execs at ~300–500/s (2,756 s), coverage + 19,894 edges / 86,455 features / 4,439 in-memory corpus entries, + **still growing at budget end** (2× the wave-1 45-min smoke edge + count). Longest campaigns keep paying on the schema side. +- `data_access` — 11.3M execs at ~3.7–4.7k/s (2,761 s), coverage 684 + edges / 6,208 features — value-profile lifted the "saturated" 379 + edges to 684 (the wave-1 ceiling was the no-profile ceiling). +- `read_opseq` — 63k execs at ~23/s (2,737 s), coverage 17,519 edges / + 65,059 features / 1,668 entries; the stateful search kept adding + features across the full budget (assertion-throughput-bound). +- `layout_build` — 63k execs at ~23/s (2,751 s), coverage 17,659 + edges / 71,477 features / 1,873 entries; write-side contracts held. +- `validate_pair` — three crashes over two runs before and one clean + run after the W3-1/W3-2/W3-3 fixes (final post-fix campaign 30 min): + 37k execs (final 1,820 s run), coverage 10,850 edges / 38,364 + features / 1,544 entries, `oom/timeout/crash: 0/0/0` at budget end. + The two-input harness is the highest-yield target in the crate: 1 + real engine bug + 2 pinned contracts in its first campaigns. +- Artifact totals: the three `validate_pair` crash artifacts are all + pinned as committed seeds/regression tests (W3-1 `seed-044`, W3-2 + `seed-047`, W3-3 reproduced by the aligned maxLength fixtures); + `bast_compile`/`data_access`/`read_opseq`/`layout_build` artifacts + empty. No OOM, timeout, or leak on any fork job of any target. + Wave-2 smoke campaign results (2026-09-30, 10 min per target, §5 policy, detached): @@ -497,17 +582,39 @@ confirms or refutes them. declaration rejected in packed mode, that is a schema-gate change with the mode-agreement invariant to re-verify. +8. **Aligned `maxLength` reservation read paths disagreed — + CONFIRMED as a real engine bug and FIXED (wave 3, finding W3-3, + found by the `validate_pair` campaign).** The materializer and + `validate_bytes` implement ADR-003 strategy 2 correctly (raw + zero-padded window, NUL-trimmed), but `AlkTypeEngine::read_field` + dispatched every aligned String/Bytes leaf through the + length-prefixed `data_access::read_string`/`read_bytes` — parsing + the reservation window's first four raw bytes as a u32 length — + and `write_field` wrote prefix+data into the raw window. Any + aligned schema declaring `maxLength` whose first reservation bytes + looked like a large prefix broke the validate_bytes ⇒ read_field + lattice (validate Ok, read Access with bogus bounds). Fixed by + recording the strategy in `LeafMeta`'s encoding + (`VariableEncoding::MaxLengthReserved`, additive variant) and + dispatching read/write through new + `data_access::read_reservation{,_string}`/`write_reservation` + sharing the materializer's exact semantics. Regression tests in + `src/engine.rs` + `data_access.rs`; the W3-1 artifact bytes are + the committed reproducer (`seed-044`). + None of these rises to the alkcall §6.2 / alkhttp FWD-20 class; they are boundary-confirmations, which is exactly the expected profile of -this crate (§1 honest caveat). Smoke-campaign evidence so far: no -panics or OOM/timeouts on any of the 383k+ combined execs (2026-09-30, -§5); candidates 1, 2, and 4 held under the parser-level drives — -candidates 1 and 2 get their explicit stateful assertions in waves +this crate (§1 honest caveat). Smoke-campaign evidence: no panics or +OOM/timeouts on any of the 383k+ wave-1/2 combined execs; the +release-budget campaigns added 12.3M+ execs with the W3 findings +above. Candidates 1, 2, and 4 held under the parser-level drives — +candidates 1 and 2 got their explicit stateful assertions in waves 2–3 (targets 3–5), and 4 keeps its boundary corpus entry. Candidate 3 (duplicate first-wins) is pinned by the wave-2 `layout_build` index assertions and the existing crate tests; candidate 6 was confirmed as a genuine bug and fixed in wave 2; candidate 7 is pinned with an open -design question. +design question; candidate 8 was confirmed as a genuine bug and fixed +in wave 3. ## 7. Sequencing @@ -520,9 +627,17 @@ design question. One real bug found and fixed first-session (`plan_read_array` bounds check, §6 candidate 6); packed-mode offset-indirect pinned as a documented no-op (§6 candidate 7). -3. **Wave 3** — target 5 `validate_pair` (the two-input structured - harness) + long (45 min) release-budget campaigns across all - targets; merge any curated inputs into seeds deliberately. +3. ✅ **Wave 3 (2026-09-30)** — target 5 `validate_pair` (the + two-input structured harness) + 48 seeds + decode-pin tests + + 45-min release-budget campaigns across all five targets. Three + findings: W3-1 (harness over-assertion corrected + pinned), W3-2 + (upstream serde_json one-ulp f64 parse drift pinned with slack), + W3-3 (real engine bug — aligned maxLength reservation misread by + `read_field`/`write_field` — fixed with `VariableEncoding:: + MaxLengthReserved` + `data_access::read_reservation*`/ + `write_reservation`, commit `a0dd3d2`). Post-fix + validate_pair campaign clean to budget end. Fuzzing complete per + §2 scope: corpus replay 30/30 is the standing gate. 4. Everything else inherited verbatim: no hosted CI, OSS-Fuzz out, no Actions/workflow files anywhere in the repo (alkcall §7.9).