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.
|
||||
|
||||
**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
|
||||
handed to subsequent sessions.
|
||||
wave 1; 3 on 2026-09-29, wave 2); target 4 implemented (2026-09-29,
|
||||
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:
|
||||
|
||||
@@ -45,6 +48,29 @@ Progress log:
|
||||
10 min, zero crash/OOM/timeout, coverage still growing at budget
|
||||
end (see §5). One harness-assert fix on the way (the quoted-`""`
|
||||
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 |
|
||||
| 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 |
|
||||
| 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)
|
||||
|
||||
@@ -276,24 +302,39 @@ the chunk-walk loop and the pending accumulator.
|
||||
duplicate-key rejection with line/column) as corpus expectations.
|
||||
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
|
||||
the WS byte adapter driven end-to-end (like alktty's
|
||||
`session_opseq` / alkcall's `manager_routing`): write-side ops
|
||||
(`write(bytes)` ≤ cap, `shutdown`, oversized-write),
|
||||
read-side ops (feed messages, close, text message, oversize
|
||||
message), close timing both sides. Asserts: backpressure
|
||||
accounting (`READ_SLOTS`/`WRITE_SLOTS`), idle-timeout resets only
|
||||
on chunk progress (WS-01 semantics), close frames only at legal
|
||||
points, EOF sentinel semantics preserved, the alkcall 0.8.1 bug
|
||||
class (a rejected duplicate must leave live state intact) never
|
||||
reachable through this adapter.
|
||||
- **`retry_throttle`:** bounded-map growth of
|
||||
`RetryAfterMiddleware` (the inlined middleware): capacity bound,
|
||||
Farthest-future drop policy, ceiling clamp, expired-first
|
||||
eviction, jitter bounds; op sequences of responses with arbitrary
|
||||
`Retry-After` values (seconds, HTTP-date, garbage).
|
||||
(well-formed chunks, oversize headers, the pending shape),
|
||||
read-side ops (binary deliveries, dribble, text, inbound read
|
||||
error, peer close), a lane-healthy requested close, shutdown vs
|
||||
close ordering, backpressure scaled 1..=8 read slots. Asserts:
|
||||
lossless inbound forwarding (forwarded == delivered exactly after
|
||||
the teardown joins, prefix mid-sequence), the WS-01 walk's
|
||||
completed-chunk count vs the declared frame count, the loud-gate
|
||||
close mapping (1002/1011/verbatim-requested + a per-cause reason,
|
||||
WS-15), lossless write framing (outbound concat == the declared
|
||||
chunk stream incl. the FWD-21 failure-carry), lane-failure
|
||||
isolation (the alkcall 0.8.1 class), and pump termination within
|
||||
the harness bound. Exposure: `byte_adapter::fuzzing`
|
||||
(`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.
|
||||
|
||||
## 4. Corpus policy
|
||||
@@ -323,8 +364,16 @@ grown corpora and artifacts gitignored. Seed menus:
|
||||
`$ref`; `additionalProperties` absent (hardening probe) and
|
||||
present both ways.
|
||||
- Stateful seeds (target 4): the semantic fixtures the campaign must
|
||||
always replay — e.g. an adopted-then-teardown interleaving, a
|
||||
close-during-stream, a throttle-map cycle at capacity.
|
||||
always replay — a write/echo flow, the oversize loud-gate after a
|
||||
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
|
||||
|
||||
@@ -376,7 +425,21 @@ artifact dirs empty):
|
||||
- Toolchain notes live in `fuzz/README.md`; nightly stays confined to
|
||||
`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
|
||||
exclude, toolchain pin, runner, seed generator, README) + targets
|
||||
@@ -389,5 +452,11 @@ artifact dirs empty):
|
||||
modules. Smoke campaign clean (see §5).
|
||||
3. **Next** — target 4 stateful wave + longer campaigns; revisit
|
||||
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.
|
||||
Reference in new issue
Block a user