Files
alkhttp/fuzz
glm-5.3-flash 0ae53d2d27 fix(websocket): WriteChunkWalk failure arms carry the parsed pieces
FWD-21, 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 silently 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. 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 skipped on exactly this path.

- Walk::Oversize(Vec<Vec<u8>>) / Walk::PendingOverCap(Vec<Vec<u8>>)
  carry the pre-failure pieces (user-approved semver-relevant API
  change)
- run_write_pump emits the carried pieces before fail_write_pump, so
  a 1011 close never swallows a legal chunk the mux already counted
  as written
- the ws_chunk_framing harness's split-drive failure arm now
  reconciles the parsed-before-failure bytes across the whole and
  split drives; a regression seed covers the drain-then-fail shape

Verification: main crate 441 default / 520 all-features tests, fuzz
shared replay 21/21 (default + no-default-features), cargo fuzz build
clean, clippy+fmt clean both crates.
2026-09-29 17:28:14 +00:00
..

alkhttp fuzzing

cargo-fuzz targets for the HTTP surface's hand-rolled parse paths. The design and operating rules live in docs/plans/fuzzing.md (adopted from alkcall's docs/research/fuzzing.md); this README is the operational cheat-sheet.

Layout

  • fuzz_targets/ — nightly-only fuzz_target! binaries (thin wrappers).
  • shared/ — stable-toolchain library holding the invariant logic; the corpus replay tests run here on plain cargo test.
  • corpus/<target>/ — committed seeds (regenerate with python3 fuzz/gen_fuzz_seeds.py).
  • artifacts/ — gitignored crash/oom/timeout artifacts + campaign logs.

Targets

Target Drives
sse_parser SseParser::feed + the FWD-17 envelope mapping (src/adapters/forward.rs) — the SSE frame parser every subscription forwards through
ws_chunk_framing WriteChunkWalk (the write pump's chunk walk) (src/websocket/byte_adapter.rs)
spec_import OpenAPISpec::from_str/from_yaml/from_json, the YAML normalization boundary, CompiledInputSchema compile/harden/validate, FromMCP::build_spec (src/adapters/openapi_spec.rs, input_validation.rs, from_mcp/mod.rs)

Running a campaign — always detached

Agent sessions must never run fuzzing in the foreground (an OOM in a target can take down the session host; see docs/plans/fuzzing.md §2). Use the detached runner:

fuzz/run-detached.sh sse_parser
# poll:
tail -n 50 fuzz/artifacts/sse_parser-*.log
ls fuzz/artifacts/sse_parser/
pgrep -f "cargo fuzz run sse_parser"

FUZZ_RUNTIME_SECS=1800 fuzz/run-detached.sh sse_parser for a longer campaign. The runner pins -fork=1 -rss_limit_mb=2048 -malloc_limit_mb=2048 -timeout=25 and detaches via setsid + nohup.

Corpus replay (the standing fuzz gate)

cargo test --manifest-path fuzz/shared/Cargo.toml

Replays every committed seed through the same invariant functions the fuzzer runs, on stable, no nightly needed. Part of the release verification checklist (AGENTS.md).

Toolchain notes

rust-toolchain.toml pins nightly for this subtree only (llvm-tools required by cargo-fuzz); the crate itself stays on stable at MSRV 1.88. cargo fuzz build must be run with the fuzz dir as CWD or trust the toolchain file (rustup resolves per directory).

Run campaigns before releases and after touching src/adapters/forward.rs (the SSE parser), src/websocket/byte_adapter.rs (the WS chunk walk), or the import adapters (src/adapters/openapi_spec.rs, input_validation.rs, from_mcp/).