docs(fuzz): record wave-3 campaign results + FWD-21
This commit is contained in:
1 parent
e023d3275e
commit
4e8a2cfddd
1 file changed
+89
-20
+89
-20
@@ -10,8 +10,11 @@ survey, CI tiers) — this plan stays focused on what alkhttp fuzzes and
|
|||||||
in what order.
|
in what order.
|
||||||
|
|
||||||
**Status:** targets 1–3 implemented and verified (1–2 on 2026-09-29,
|
**Status:** targets 1–3 implemented and verified (1–2 on 2026-09-29,
|
||||||
wave 1; 3 on 2026-09-29, wave 2); target 4 is planned and can be
|
wave 1; 3 on 2026-09-29, wave 2); target 4 implemented (2026-09-29,
|
||||||
handed to subsequent sessions.
|
wave 3: `ws_session_opseq` + `retry_throttle`; campaign results in
|
||||||
|
§5); the only crash found so far (FWD-21, the `WriteChunkWalk`
|
||||||
|
failure-carry drop) was a crate contract fix, not a harness
|
||||||
|
misassert.
|
||||||
|
|
||||||
Progress log:
|
Progress log:
|
||||||
|
|
||||||
@@ -45,6 +48,29 @@ Progress log:
|
|||||||
10 min, zero crash/OOM/timeout, coverage still growing at budget
|
10 min, zero crash/OOM/timeout, coverage still growing at budget
|
||||||
end (see §5). One harness-assert fix on the way (the quoted-`""`
|
end (see §5). One harness-assert fix on the way (the quoted-`""`
|
||||||
YAML key noted above).
|
YAML key noted above).
|
||||||
|
- **2026-09-29 — target 4 (`ws_session_opseq` + `retry_throttle`)
|
||||||
|
implemented (wave 3).** Two stateful harnesses + 16 seeds + the
|
||||||
|
`byte_adapter::fuzzing` drive hub (generic pump bodies over a mock
|
||||||
|
socket flavor) + the `client::fuzzing` throttle hub. One crate
|
||||||
|
finding on the way: FWD-21 (above) — fixed same commit.
|
||||||
|
- **2026-09-29 — FWD-21, the `WriteChunkWalk` failure-carry drop.**
|
||||||
|
Found minutes into the first `ws_session_opseq` campaign: one feed
|
||||||
|
that drains a complete chunk and then hits an oversize header (or
|
||||||
|
the pending cap) in the same call returned
|
||||||
|
`Walk::Oversize`/`PendingOverCap` and **discarded the already
|
||||||
|
drained pieces** — the walk's own consumption-accounting contract
|
||||||
|
("every parsed byte is either emitted or retained", wave-1
|
||||||
|
invariant 6) was violated by the crate, not the harness. Production
|
||||||
|
could not reach the shape (the mux's per-write invariant caps
|
||||||
|
`pending` at one chunk + header), so impact was nil, but
|
||||||
|
`WriteChunkWalk` is public API and the wave-1 `split_walk` equality
|
||||||
|
check was silently skipped on exactly this path. Fix (user-approved
|
||||||
|
semver-relevant API change): `Walk::Oversize(Vec<Vec<u8>>)` /
|
||||||
|
`Walk::PendingOverCap(Vec<Vec<u8>>)` carry the pre-failure pieces;
|
||||||
|
the write pump emits them before `fail_write_pump`, so a 1011
|
||||||
|
close never swallows a legal chunk the mux already counted as
|
||||||
|
written. Regression seeds cover the drain-then-fail shape in both
|
||||||
|
chunk harnesses.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -141,7 +167,7 @@ checklist, as the siblings did.
|
|||||||
| 1 | `sse_parser` | `SseParser::feed` + `sse_event_envelope` mapping (`src/adapters/forward.rs`) | chunk-split sequence × eof flag | implemented 2026-09-29 |
|
| 1 | `sse_parser` | `SseParser::feed` + `sse_event_envelope` mapping (`src/adapters/forward.rs`) | chunk-split sequence × eof flag | implemented 2026-09-29 |
|
||||||
| 2 | `ws_chunk_framing` | the WS↔byte-adapter chunk parsers (`WriteChunkWalk`, the write pump's extracted chunk walk) (`src/websocket/byte_adapter.rs`) | byte sequences + message-split boundaries | implemented 2026-09-29 |
|
| 2 | `ws_chunk_framing` | the WS↔byte-adapter chunk parsers (`WriteChunkWalk`, the write pump's extracted chunk walk) (`src/websocket/byte_adapter.rs`) | byte sequences + message-split boundaries | implemented 2026-09-29 |
|
||||||
| 3 | `spec_import` | `OpenAPISpec::from_str`/`from_yaml`/`from_json`, `CompiledInputSchema`/`harden_closed_by_default`/`validate`, `FromMCP::build_spec` (`src/adapters/openapi_spec.rs`, `from_jsonschema.rs`, `from_mcp/mod.rs`) | attacker-shaped OpenAPI/JSON/YAML docs + schemas | implemented 2026-09-29 |
|
| 3 | `spec_import` | `OpenAPISpec::from_str`/`from_yaml`/`from_json`, `CompiledInputSchema`/`harden_closed_by_default`/`validate`, `FromMCP::build_spec` (`src/adapters/openapi_spec.rs`, `from_jsonschema.rs`, `from_mcp/mod.rs`) | attacker-shaped OpenAPI/JSON/YAML docs + schemas | implemented 2026-09-29 |
|
||||||
| 4 | `ws_session_opseq` / `retry_throttle` | stateful op sequences over the WS byte-adapter pumps (write/read pump interleavings, close timing, oversize frames); `RetryAfterMiddleware` throttle-map growth | `#[derive(Arbitrary)]` op enums | planned (second wave) |
|
| 4 | `ws_session_opseq` / `retry_throttle` | stateful op sequences over the WS byte-adapter pumps (write/read pump interleavings, close timing, oversize frames); `RetryAfterMiddleware` throttle-map growth | `#[derive(Arbitrary)]` op enums | implemented 2026-09-29 |
|
||||||
|
|
||||||
### Target 1 — `sse_parser` (the chunk_header-class target)
|
### Target 1 — `sse_parser` (the chunk_header-class target)
|
||||||
|
|
||||||
@@ -276,24 +302,39 @@ the chunk-walk loop and the pending accumulator.
|
|||||||
duplicate-key rejection with line/column) as corpus expectations.
|
duplicate-key rejection with line/column) as corpus expectations.
|
||||||
42 committed seeds cover the menu below.
|
42 committed seeds cover the menu below.
|
||||||
|
|
||||||
### Target 4 — stateful wave (planned)
|
### Target 4 — stateful wave (implemented 2026-09-29)
|
||||||
|
|
||||||
- **`ws_session_opseq`:** `#[derive(Arbitrary)]` op sequences over
|
- **`ws_session_opseq`:** `#[derive(Arbitrary)]` op sequences over
|
||||||
the WS byte adapter driven end-to-end (like alktty's
|
the WS byte adapter driven end-to-end (like alktty's
|
||||||
`session_opseq` / alkcall's `manager_routing`): write-side ops
|
`session_opseq` / alkcall's `manager_routing`): write-side ops
|
||||||
(`write(bytes)` ≤ cap, `shutdown`, oversized-write),
|
(well-formed chunks, oversize headers, the pending shape),
|
||||||
read-side ops (feed messages, close, text message, oversize
|
read-side ops (binary deliveries, dribble, text, inbound read
|
||||||
message), close timing both sides. Asserts: backpressure
|
error, peer close), a lane-healthy requested close, shutdown vs
|
||||||
accounting (`READ_SLOTS`/`WRITE_SLOTS`), idle-timeout resets only
|
close ordering, backpressure scaled 1..=8 read slots. Asserts:
|
||||||
on chunk progress (WS-01 semantics), close frames only at legal
|
lossless inbound forwarding (forwarded == delivered exactly after
|
||||||
points, EOF sentinel semantics preserved, the alkcall 0.8.1 bug
|
the teardown joins, prefix mid-sequence), the WS-01 walk's
|
||||||
class (a rejected duplicate must leave live state intact) never
|
completed-chunk count vs the declared frame count, the loud-gate
|
||||||
reachable through this adapter.
|
close mapping (1002/1011/verbatim-requested + a per-cause reason,
|
||||||
- **`retry_throttle`:** bounded-map growth of
|
WS-15), lossless write framing (outbound concat == the declared
|
||||||
`RetryAfterMiddleware` (the inlined middleware): capacity bound,
|
chunk stream incl. the FWD-21 failure-carry), lane-failure
|
||||||
Farthest-future drop policy, ceiling clamp, expired-first
|
isolation (the alkcall 0.8.1 class), and pump termination within
|
||||||
eviction, jitter bounds; op sequences of responses with arbitrary
|
the harness bound. Exposure: `byte_adapter::fuzzing`
|
||||||
`Retry-After` values (seconds, HTTP-date, garbage).
|
(`FuzzMockSocket` flavor, `FuzzChannels`/`FuzzWriteSide` channel
|
||||||
|
bundle, pump spawn funnels, `FuzzInboundChunkProgress`).
|
||||||
|
- **`retry_throttle`:** bounded-map growth of the inlined
|
||||||
|
`RetryAfterMiddleware`: capacity bound (exact, distinct-URL
|
||||||
|
observable partition), expired-first + Farthest-future eviction
|
||||||
|
policy, ceiling clamp (deadline never grows past the ceiling of
|
||||||
|
the record moment), refresh contraction (a higher `Retry-After`
|
||||||
|
never extends the first-seen deadline; the missing-header refresh
|
||||||
|
is a documented no-op), `Retry-After` parse partition
|
||||||
|
(non-negative seconds ∪ future HTTP dates, post-clamp), and the
|
||||||
|
jitter bounds (≤ 25% of a short wait, capped at 2 s). Op sequences
|
||||||
|
of records/refreshes/queries/rebuilds with seconds, huge-seconds,
|
||||||
|
HTTP-date, past-date, garbage, and no-header shapes. Exposure:
|
||||||
|
`client::fuzzing` (constructor, synthetic throttled response,
|
||||||
|
record/deadline/parse/jitter funnels — the middleware map never
|
||||||
|
leaks).
|
||||||
- Sequenced after 1–3 stabilize, per the sibling pattern.
|
- Sequenced after 1–3 stabilize, per the sibling pattern.
|
||||||
|
|
||||||
## 4. Corpus policy
|
## 4. Corpus policy
|
||||||
@@ -323,8 +364,16 @@ grown corpora and artifacts gitignored. Seed menus:
|
|||||||
`$ref`; `additionalProperties` absent (hardening probe) and
|
`$ref`; `additionalProperties` absent (hardening probe) and
|
||||||
present both ways.
|
present both ways.
|
||||||
- Stateful seeds (target 4): the semantic fixtures the campaign must
|
- Stateful seeds (target 4): the semantic fixtures the campaign must
|
||||||
always replay — e.g. an adopted-then-teardown interleaving, a
|
always replay — a write/echo flow, the oversize loud-gate after a
|
||||||
close-during-stream, a throttle-map cycle at capacity.
|
clean chunk, the text-rejection + surviving write lane, an inbound
|
||||||
|
read error, a peer close mid-flight + shutdown drain, a requested
|
||||||
|
close, the throttle-map fill/evict cycle at capacity, the
|
||||||
|
missing-header refresh no-op, hostile-seconds clamping, refresh
|
||||||
|
contraction, and a mid-sequence rebuild (16 total; the
|
||||||
|
`ws_session_opseq`/`retry_throttle` blocks in
|
||||||
|
`fuzz/gen_fuzz_seeds.py`, hand-encoded against the `arbitrary`
|
||||||
|
1.4.x derive layout with ceiling-division tags and per-element
|
||||||
|
keep-going bytes, pinned by seed-decode tests).
|
||||||
|
|
||||||
## 5. Campaign + gate policy
|
## 5. Campaign + gate policy
|
||||||
|
|
||||||
@@ -376,7 +425,21 @@ artifact dirs empty):
|
|||||||
- Toolchain notes live in `fuzz/README.md`; nightly stays confined to
|
- Toolchain notes live in `fuzz/README.md`; nightly stays confined to
|
||||||
`fuzz/`.
|
`fuzz/`.
|
||||||
|
|
||||||
## 6. Sequencing
|
Wave-3 stateful smoke campaigns (2026-09-29, 10 min per target, both
|
||||||
|
exited 0, artifact dirs empty — no crash/hang/OOM/leak; the first
|
||||||
|
`ws_session_opseq` launch aborted at 17 s on FWD-21, fixed before
|
||||||
|
the counted run):
|
||||||
|
|
||||||
|
- `ws_session_opseq` — 206k execs at ~340/s (each exec interleaves up
|
||||||
|
to 64 two-lane ops with yield quiesce points per exec, so per-exec
|
||||||
|
work is heavy), coverage 2251 edges / 5831 features / 232 in-memory
|
||||||
|
corpus entries — coverage still growing at budget end.
|
||||||
|
- `retry_throttle` — 897k execs at ~1.4k/s, coverage saturated at
|
||||||
|
1154 edges / 6925 features / 324 corpus entries.
|
||||||
|
- **Seeds regenerate deterministically:** `python3
|
||||||
|
fuzz/gen_fuzz_seeds.py`.
|
||||||
|
- Toolchain notes live in `fuzz/README.md`; nightly stays confined to
|
||||||
|
`fuzz/`.
|
||||||
|
|
||||||
1. ✅ **Done (2026-09-29, commit 5971c0a)** — infra (workspace
|
1. ✅ **Done (2026-09-29, commit 5971c0a)** — infra (workspace
|
||||||
exclude, toolchain pin, runner, seed generator, README) + targets
|
exclude, toolchain pin, runner, seed generator, README) + targets
|
||||||
@@ -389,5 +452,11 @@ artifact dirs empty):
|
|||||||
modules. Smoke campaign clean (see §5).
|
modules. Smoke campaign clean (see §5).
|
||||||
3. **Next** — target 4 stateful wave + longer campaigns; revisit
|
3. **Next** — target 4 stateful wave + longer campaigns; revisit
|
||||||
corpus merges if any campaign yields inputs worth curating.
|
corpus merges if any campaign yields inputs worth curating.
|
||||||
|
4. ✅ **Done (2026-09-29, wave 3)** — target 4 stateful
|
||||||
|
(`ws_session_opseq` + `retry_throttle`): two harnesses + 16 seeds
|
||||||
|
+ the `byte_adapter::fuzzing` / `client::fuzzing` hubs + FWD-21
|
||||||
|
(the `WriteChunkWalk` failure-carry drop — a crate contract fix;
|
||||||
|
user-approved semver-relevant API change). Smoke campaigns clean
|
||||||
|
(see §5). Longer (30+ min) campaigns before the next release.
|
||||||
|
|
||||||
Status updates land in §3's table and §6's list as work completes.
|
Status updates land in §3's table and §6's list as work completes.
|
||||||
Reference in new issue
Block a user