fuzz: adopt fuzzing (wave 1) + alkcall 0.8.1 / alktunnels 0.2.1 bumps

Adopt the alk*/fuzzing posture (alkcall 0.8.1 + alktunnels 0.2.1
precedent) before the v0.1 publish — the cheapest moment this crate
will ever have for it:

- alkcall 0.8.1 + alktunnels 0.2.1: the duplicate-adopt/open fix is
  directly relevant to this crate's consumer open/adopt paths
- fuzz/ workspace (the sibling layout): nightly-pinned subtree, stable
  fuzz/shared library (corpus replay = the fuzz gate), detached runner,
  JSON dict, deterministic seed generator; 129 committed seeds
- three wave-1 targets over already-pub APIs: datagram_codec (the
  ASSOCIATE frame codec — the alkcall-fuzz-doc-assigned downstream
  target), rfc1928_codec (the forked RFC 1928 core's handshake state
  machines, both protocol directions), params_parse (the wire-stable
  open-op surface)
- one diagnostic: DatagramReader::retained_bytes() (the decoder-state
  bound's assertion target, the alktunnels retained_bytes() posture)
- docs/research/fuzzing.md: the scoped plan + implementation log;
  AGENTS.md gains the corpus-replay verification line
- campaigns: 3 × 10-min detached runs exited 0 (datagram ~1.9M execs,
  rfc1928 ~2.0M, params ~3.1M; oom/timeout/crash 0/0/0 on all 33 fork
  jobs per target; coverage saturated on both codec targets). No crate
  bugs; three harness-model defects found + fixed (2 pre-campaign by
  corpus replay, 2 campaign-found, crash inputs promoted as seeds 900/
  901): the len==0 well-framed decode partition, the fieldless-struct
  serde-seq form, the BadAtyp re-frame arm, the short-domain count-walk
  gate
- wave 2 (session_pumps stateful target) deferred to post-0.1 — the
  byte-level targets carry the attacker-reachable load for the publish

Verification: cargo test (15 suites ok, default + all-features), clippy
(all-targets + wasm + fuzz/shared), fmt, cargo doc, publish --dry-run,
corpus replay 129 seeds green, cargo fuzz build clean.

Refs: alkcall docs/research/fuzzing.md (§7.9 posture), alktunnels
docs/research/fuzzing.md; docs/research/fuzzing.md (this crate's copy)
This commit is contained in:
glm-5.3-flash committed 2026-09-28 11:18:08 +00:00
1 parent 8af23eeee0
commit 08a3694464
149 files changed
+1822 -5

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
target
corpus/*
!corpus/datagram_codec
!corpus/rfc1928_codec
!corpus/params_parse
artifacts
coverage
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "alksocks-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
alksocks-fuzz-shared = { path = "shared" }
[dependencies.alksocks]
path = ".."
[[bin]]
name = "datagram_codec"
path = "fuzz_targets/datagram_codec.rs"
test = false
doc = false
bench = false
[[bin]]
name = "rfc1928_codec"
path = "fuzz_targets/rfc1928_codec.rs"
test = false
doc = false
bench = false
[[bin]]
name = "params_parse"
path = "fuzz_targets/params_parse.rs"
test = false
doc = false
bench = false
[workspace]
+55
View File
@@ -0,0 +1,55 @@
# alksocks fuzzing
libFuzzer targets for alksocks' wire surfaces (see
`docs/research/fuzzing.md` for the full rationale and campaign plan).
## Layout
- `fuzz_targets/` — one binary per target; thin `fuzz_target!` wrappers.
- `shared/` — the invariant logic, as a plain library so normal
`cargo test` (stable toolchain) can replay the committed corpora
through the same invariants (`fuzz/shared/src/*.rs` `#[cfg(test)]`
modules; quinn's CI pattern). The fuzz binaries are nightly-only;
the shared crate is stable-clean.
- `corpus/<target>/` — committed hand-made seeds (regenerate with
`python3 fuzz/gen_fuzz_seeds.py` from the repo root). Grown corpora
and artifacts are gitignored.
- `run-detached.sh` — mandatory runner for agent sessions: wraps
`cargo fuzz run` in `setsid` + `nohup` + log redirection so an OOM
in a target can never take down the agent host (research doc §5).
- `json.dict` — JSON token dictionary for the params target.
## Targets
| Target | Drives | Invariants |
|---|---|---|
| `datagram_codec` | `DatagramReader::feed` / `frame` / `frame_inner` (`src/wire.rs`) | no-panic; infallible reassembly with the decode-error partition; empty-payload F-2 layering; bad-ATYP loudness; splitting transparency; round-trips; the joint bound (incl. the 65766 shape); the decoder-state retained-bytes bound |
| `rfc1928_codec` | the forked RFC 1928 core (`src/rfc1928/`): `read_address`, the server typestate handshake, the RFC 1929 sub-handshake, the RFC UDP header codec, `ReplyError::from_u8` | no-panic on any byte sequence, both protocol directions; typed failures only (the F-1 class); round-trips; the full-domain reply table |
| `params_parse` | `Socks5Params` / `socks5_open_spec()` / the reason surface (`src/params.rs`) | no-panic; the trivial-params pin; the spec is a constant (the wire-stable ADR-001 surface + the schema/typed-view drift guard); the pass-through reason-extraction posture |
## Commands
`cargo fuzz build` and `cargo fuzz run` must be executed with `fuzz/`
(or deeper) as the working directory so rustup selects the pinned
nightly toolchain — the detached runner handles that itself.
```sh
# build (nightly, pinned by rust-toolchain.toml inside fuzz/; run from fuzz/)
cargo fuzz build
# agent sessions: detached campaign (never foreground; CWD-independent)
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh datagram_codec -max_len=65536
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh rfc1928_codec -max_len=4096 -use_value_profile=1
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh params_parse -dict=json.dict -use_value_profile=1
# corpus replay through the invariants (stable toolchain, no nightly)
cargo test --manifest-path fuzz/shared/Cargo.toml
# coverage
cargo fuzz coverage <target>
```
The fuzz workspace is excluded from the main workspace and from the
published package (`exclude` in the root `Cargo.toml`); it pins its own
nightly toolchain via `rust-toolchain.toml` and does not affect the
crate's stable MSRV.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{}
+1
View File
@@ -0,0 +1 @@
{"resource": "sneaky"}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "dial_failed"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "made_up_reason"}}
+1
View File
@@ -0,0 +1 @@
{"code": "FORBIDDEN", "details": {"reason": "no"}}
+1
View File
@@ -0,0 +1 @@
{"reason": {"enum": ["dial_failed", 1]}}
+1
View File
@@ -0,0 +1 @@

+1
View File
@@ -0,0 +1 @@
[]
+1
View File
@@ -0,0 +1 @@
{"nested": {"deep": {"deeper": [1, {"a": null}]}}}
+1
View File
@@ -0,0 +1 @@
{"channel_open": {"alpn": "alk/tunnel"}}
+1
View File
@@ -0,0 +1 @@
{"access_control": {"required_scopes": ["socks5:open"]}}
+1
View File
@@ -0,0 +1 @@
{"details": {"reason": 1}}
+1
View File
@@ -0,0 +1 @@
{"details": {"reason": {"enum": "dial_failed"}}}
+1
View File
@@ -0,0 +1 @@
{"code": {"channel:open_failed": true}, "details": null}
+1
View File
@@ -0,0 +1 @@
{"type": "object", "additionalProperties": false}
+1
View File
@@ -0,0 +1 @@
"{}"
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
{"a": {"a": {"a": {"a": {"a": {"a": {"a": {"a": {"a": {"a": {"a": {"a": 1}}}}}}}}}}}}
Binary file not shown.
+1
View File
@@ -0,0 +1 @@

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@

Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@

+1
View File
@@ -0,0 +1 @@

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
�uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu�ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
Binary file not shown.
Binary file not shown.
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff. Show more