4 Commits
Author SHA1 Message Date
glm-5.3-flash 1365272146 release: v0.3.0 — the dependency-major (alkcall 0.9.0)
- Cargo.toml: alkcall 0.8.1 -> 0.9.0 (crates.io, the stub-sweep
  release) and 0.2.1 -> 0.3.0 (breaking dep bump + public alkcall
  re-exports -> minor, matching the 0.7.1 -> 0.8.0 ride on 0.2.0)
- fuzz/shared/Cargo.toml: alkcall 0.9.0 (path-linked alktunnels must
  see one alkcall version)
- src/producer.rs + tests/producer_direct_op.rs: the only code contact
  with the 0.9.0 breaking surface — Establishment no longer derives
  Debug; two expect_err calls -> .err().expect, one untyped
  panic!("{other:?}") fall-through -> typed Err/Ok arms
- CHANGELOG: 0.3.0 section + compare links
- README: the 0.3.0 note
- docs/architecture/overview.md: dep tree alkcall 0.9.0

Verification: cargo test (136 default / 163 with local), fuzz/shared
corpus replay (0 pass, empty target set — no regressions), clippy
--all-targets -D warnings (native + wasm32-unknown-unknown), fmt
--check, wasm32 check, and cargo publish --dry-run --allow-dirty all
green
2026-10-14 05:23:08 +00:00
glm-5.3-flash 05b25465aa release: v0.2.1 — the hardening (fuzzing adopted + alkcall 0.8.1)
- CHANGELOG: Unreleased cut to 0.2.1 (the fuzz/ workspace: three
  cargo-fuzz targets + stable-toolchain corpus replay in fuzz/shared;
  the two wave-2 diagnostics datagram_backlog/retained_bytes; the
  alkcall 0.8.0 -> 0.8.1 bump — duplicate adopt no longer destroys the
  live channel; compare links updated)
- README: the 0.2.1 hardening note (fuzzing + alkcall 0.8.1 + the two
  additive diagnostics)
- docs/architecture/overview.md: dep tree alkcall 0.8.1
- Cargo.toml: 0.2.0 -> 0.2.1

Verification: cargo test (136 default / 163 with local), fuzz/shared
corpus replay (5 pass), clippy --all-targets -D warnings (default,
all-features, wasm32-unknown-unknown, fuzz/shared), fmt --check, doc,
wasm32 check, and cargo publish --dry-run --allow-dirty all green
(42 packaged files — fuzz/, docs/research|reviews|plans/, tasks/,
.opencode/, AGENTS.md all excluded)
2026-09-28 10:21:48 +00:00
glm-5.3-flash e3c6f6c9f8 fuzz: wave 2 — session_pumps stateful two-pump target
The wave-2 stateful target from docs/research/fuzzing.md 3.3: op
sequences (arbitrary SessionSequence) over one live channels stack
per exec - MuxRunner -> raw chunk stream -> the real
run_demux_loop_for_client demux -> ChannelManager per side (producer
Connect / consumer Accept over one duplex transport, the reverse
POC's topology). The harness mirrors TunnelSession's data-plane
shapes: datagram planes (codec reader + pending, the
DatagramHalves mirror) and stream planes (pump_bidi against
in-process peer halves, the pump_against mirror).

Invariants (3.3): no-panic/no-wedge (5s reconcile join); two-pump
shutdown-on-completion; lossless p2c copy count; EOF-sentinel
teardown semantics (drain-before-EOF, map empties); FIFO datagram
reconciliation through the real demux path (stateful framing walk,
the wave-1 walk carried across ops); the 1.3 DatagramHalves.pending
backlog bound (<= routed-but-unpulled remainder); decoder-state
retained-bytes bound re-asserted per op.

Harness-model findings (all six wave findings are harness defects,
not crate bugs - the fuzzer-as-harness-checker pattern):
- duplicate adopt is a legal ChannelExists no-op (documented table)
- the p2c-EOF pump join is the DESIGNED half-close shape; the pump
  completes once the c2p source EOFs too (teardown), not before
- route_payload routes into the LOCAL manager (demux direction);
  the producer pump writes the channel send half - the first
  campaign's bytes never reached the consumer; rewritten to the
  real protocol shape (Open allocates, session-index addressing,
  stateful framing model, Pull op, EOF sentinels only from
  teardown/sender-drop)
- clear_all's return is the opener-ledger records, NOT the reaped
  ids (adopted channels carry no ledger entry - the alkcall 7.7
  lesson re-learned); the map-empties assert is the ground truth

Campaign (10-min detached, 6 runner): 228,676 execs, 32 fork jobs,
oom/timeout/crash 0/0/0, exit 0. Coverage 3427 edges / 17.5k
features still expanding at budget end. No crate bugs found - the
two-pump contract, EOF-sentinel semantics, and the pending backlog
bound are demonstrated properties.

Crate addition: TunnelSession::datagram_backlog() (the
DatagramHalves.pending diagnostic the 3.3 bound asserts against, in
the retained_bytes() style) + a suite test pinning its rest states.

Verification: main suite 136 passed; corpus replay (8 committed
seeds + 2 semantic fixtures) green; clippy -D warnings + fmt clean
(main, shared, wasm); wasm check clean; cargo fuzz build clean.
2026-09-28 10:12:09 +00:00
glm-5.3-flash 57285d23bd fuzz: adopt fuzzing (wave 1) + alkcall 0.8.1 bump
Bump the substrate dependency to alkcall 0.8.1 — the bug-fix release
whose fix (duplicate adopt/open no longer destroying the live channel)
is directly relevant to this crate's reverse-path adopt flow.

Adopt fuzzing per docs/research/fuzzing.md, scoped smaller than
alkcall's (the envelope/chunk/demux/manager surfaces are upstream and
now fuzzed there; this crate fuzzes its own named downstream target —
the ADR-003 datagram codec — plus the params/reply parsers):

- fuzz/ workspace per the alkcall pattern: nightly-pinned subtree
  (rust-toolchain.toml), thin fuzz_target! binaries, and a
  stable-toolchain shared crate carrying the invariant logic so the
  committed corpus replays as plain `cargo test` (the fuzz gate in
  the release checklist — no hosted CI, alkcall §7.9 posture).
- Targets (both over already-pub APIs; no #[cfg(fuzzing)] exposure):
  - datagram_codec — DatagramReader::feed + frame_datagram: no-panic,
    infallible decode, size bound, F-2 sentinel layering (empty
    datagram only from a length-0 header at a frame boundary),
    3-byte-slice splitting transparency, re-frame round-trip,
    oversize rejection at the bound, is_mid_datagram framing state,
    and the decoder-state retained-bytes bound.
  - params_parse — serde params/reply parsers + establishment_reason:
    no-panic on raw bytes and bounded structured Values, clean typed
    failure, round-trips, listen: 0 pin, loud/lenient projection-pair
    agreement, pass-through reason extraction model.
- New public diagnostic: DatagramReader::retained_bytes() (decoder-
  state byte count; the fuzz bound asserts against it).
- 96 committed seeds (gen_fuzz_seeds.py) + the campaign-found crash
  input promoted as datagram_codec/seed-900; grown corpora gitignored.
- run-detached.sh: setsid+nohup runner so campaigns never share fate
  with the agent session process tree (fuzzing.md §5).

Campaign findings this session were harness-model defects, not crate
bugs (both invisible to example-based review; the alkcall
"fuzzer as harness-checker" pattern confirmed):

- the F-2 empty-datagram invariant first counted [00 00] pairs in the
  raw stream — a pair inside a payload region is payload, not a
  header; corrected to walk the framing (crash artifact saved).
- establishment_reason is a pass-through extractor of details.reason,
  not a validated table (harness first asserted a closed set).
- is_mid_datagram means a partial datagram, not buffered header bytes
  (a 1-byte header fragment is buffered, not in flight).

Verification: cargo test 135 passed; clippy -D warnings and fmt clean
(main + fuzz/shared); corpus replay green; cargo fuzz build clean;
wasm32-unknown-unknown check clean. Wave-1 campaigns (10-min detached,
fork mode): params_parse 3.14M execs / datagram_codec 827k execs,
0 crashes/OOMs/timeouts; coverage saturated on the codec state space.
2026-09-28 08:09:11 +00:00
133 changed files with 3794 additions and 11 deletions

No files matched your search

+83 -1
View File
@@ -6,6 +6,86 @@ this crate adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
## [0.3.0] - 2026-10-14
The dependency-major release: the alkcall substrate bumped 0.8.1 →
0.9.0 (the stub-sweep release). No alktunnels API change, no wire
change; the only code contact with the 0.9.0 breaking surface was
test-side (`Establishment` no longer derives `Debug` — three
`expect_err`/`panic!` sites restructured), which is why this is a
minor bump rather than a patch: the semver-incompatible dependency
bump plus this crate's public re-exports of alkcall types make it
semver-visible to downstream consumers (matching the 0.7.1 → 0.8.0
ride on 0.2.0).
### Changed
- **alkcall 0.8.1 → 0.9.0** (crates.io). The stub-sweep release: the
marked-pub open contract (S-01), `channel/control` (S-02),
`channel/resources/subscribe` (S-03), and `op/register`
forward-through (S-04) replace the four always-failing stubs.
None of the four stubs were on this crate's paths (no marked-pub
registration, no control-handle subscription, no resource
enumerator use, no generic `op/register` announcements), so no
behavior change here. Of the 0.9.0 breaking list,
`Establishment` dropping its `Debug` derive was the only surface
this crate touched — two `bindfirst_establisher` unit tests'
`expect_err` calls and one integration test's untyped
`panic!("{other:?}")` fall-through restructured (the error type
itself remains `Debug`; the `Ok` arm now formats via `panic!("got
Ok")`). `ManagerError::InvalidAdopt` (additive),
`RelayError::PubTypedOpen` removal, and the
`register_imported`/`register_imported_all` return change are
untouched by this crate's API use. 136 tests pass; wasm-clean
verified.
## [0.2.1] - 2026-09-28
The hardening release: fuzzing adopted from the alkcall pattern
(`fuzz/` workspace, excluded from the published package) and the
alkcall substrate bumped 0.8.0 → 0.8.1. No API break, no wire change.
### Added
- **Fuzzing (`fuzz/` workspace, `docs/research/fuzzing.md`)** — three
cargo-fuzz targets over this crate's own attacker-reachable surfaces
(the envelope/chunk/manager layer is upstream and fuzzed there per
alkcall 0.8.1): `datagram_codec` (the ADR-003 codec: no-panic,
infallible decode, size bound, F-2 sentinel layering, re-frame
round-trip, retained-bytes bound), `params_parse` (the serde
params/reply parsers: no-panic on raw bytes and bounded structured
values, clean typed failure, round-trips), and `session_pumps` (the
wave-2 stateful target: op sequences over a live channels stack per
exec, the `TunnelSession` data planes mirrored, no-wedge +
shutdown-on-completion + lossless p2c + EOF-sentinel teardown + the
`pending` backlog bound asserted). Invariant logic lives in
`fuzz/shared/` — a stable-toolchain crate replayed as plain
`cargo test`, so the committed corpora (96 + 8 seeds, plus the
campaign-promoted `seed-900`) stay executable without nightly.
`fuzz/` is excluded from the workspace and from the published
package. Campaigns (detached, fork mode): ~25M+228k execs across
waves 1–2, zero crashes/hangs/OOMs; all findings were
harness-model defects, no crate bugs.
- **`TunnelSession::datagram_backlog()`** — the UDP `pending` count
(the datagrams decoded from a chunk beyond the one returned), the
wave-2 harness's asserted backlog bound made caller-visible;
`None` for stream sessions or after the halves were taken.
- **`DatagramReader::retained_bytes()`** — the decoder-state byte
count (leftover buffer + the in-flight partial datagram); post-feed
bounded by one frame (≤ `MAX_DATAGRAM_LEN` + 2), pinned by the
`datagram_codec` target.
### Changed
- **alkcall 0.8.0 → 0.8.1** (crates.io). The bug-fix release: a
duplicate `adopt_channel`/`open_channel` no longer destroys the
live channel it collided with (found by alkcall's own
`manager_routing` fuzz target) — directly relevant to this crate's
reverse-path adopt flow. No alktunnels API or wire change in this
bump; the full suite passes unchanged.
## [0.2.0] - 2026-09-20
The graduation release: the direct op + the bind-first/forwarded
@@ -221,6 +301,8 @@ complete (Phase 2, review 001 findings remediated).
matrix). Wasm-clean default crate verified (`wasm32-unknown-unknown`
check + clippy).
[Unreleased]: https://git.alk.dev/alkdev/alktunnels/compare/v0.2.0...HEAD
[Unreleased]: https://git.alk.dev/alkdev/alktunnels/compare/v0.3.0...HEAD
[0.3.0]: https://git.alk.dev/alkdev/alktunnels/compare/v0.2.1...v0.3.0
[0.2.1]: https://git.alk.dev/alkdev/alktunnels/compare/v0.2.0...v0.2.1
[0.2.0]: https://git.alk.dev/alkdev/alktunnels/compare/v0.1.0...v0.2.0
[0.1.0]: https://git.alk.dev/alkdev/alktunnels/releases/tag/v0.1.0
Generated
+3 -3
View File
@@ -27,9 +27,9 @@ dependencies = [
[[package]]
name = "alkcall"
version = "0.8.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8695d47cee5b5a5fe68471f04a505bc257a7e7ed9e64d827de33ff83adf4db12"
checksum = "a3562f1f7b348cdeb3d9181eaf57b0a2fb3f988d4280e8e225dadfd7f763cb84"
dependencies = [
"async-trait",
"bytes",
@@ -48,7 +48,7 @@ dependencies = [
[[package]]
name = "alktunnels"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"alkcall",
"bytes",
+7 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "alktunnels"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
rust-version = "1.88"
license = "MIT OR Apache-2.0"
@@ -9,7 +9,11 @@ repository = "https://git.alk.dev/alkdev/alktunnels"
readme = "README.md"
keywords = ["tunnel", "proxy", "channels", "alkcall", "network"]
categories = ["network-programming", "asynchronous"]
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/research/", "docs/plans/", "tasks/", "docs/sdd_process.md"]
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/research/", "docs/plans/", "tasks/", "docs/sdd_process.md", "fuzz/"]
[workspace]
members = ["."]
exclude = ["fuzz"]
[lib]
name = "alktunnels"
@@ -19,7 +23,7 @@ default = []
local = ["tokio/net"]
[dependencies]
alkcall = "0.8.0"
alkcall = "0.9.0"
tokio = { version = "1", default-features = false, features = ["rt", "sync", "io-util", "macros", "time"] }
bytes = "1"
futures = "0.3"
+16
View File
@@ -300,6 +300,22 @@ enabling it on wasm is a compile error.
flavor-form op ids in discovery (hub relay wraps them as channels);
bonus: in-tree `ChannelRelay`/`HubLegTemplate` (alkcall ADR-051).
0.2.1 is the hardening release: the `fuzz/` workspace (three
cargo-fuzz targets over the datagram codec, the params parsers, and a
stateful session-pump target; corpora replay as plain `cargo test`) —
`docs/research/fuzzing.md` — and the alkcall bump 0.8.0 → 0.8.1
(duplicate adopt/open no longer destroys the live channel — relevant
to the reverse-path adopt flow). Two additive diagnostics:
`TunnelSession::datagram_backlog()` and
`DatagramReader::retained_bytes()`. No API break, no wire change.
0.3.0 rides the alkcall bump 0.8.1 → 0.9.0 (the stub-sweep release —
the four registered-but-always-failing stubs became real
implementations; none were on this crate's paths). No alktunnels API
change, no wire change; minor rather than patch because the
semver-incompatible dependency bump + this crate's alkcall type
re-exports make it semver-visible downstream.
## Features
| Feature | Contents | wasm |
+1 -1
View File
@@ -109,7 +109,7 @@ establisher + the forwarded op (ADR-008).
```
alktunnels (default — wasm-clean)
├── alkcall 0.8.0 (core types, channels: ChannelCore/ChannelClient/manager,
├── alkcall 0.9.0 (core types, channels: ChannelCore/ChannelClient/manager,
│ pump_bidi, ServingConfig identity seam — CF-005/006,
│ establisher reply projection + flavor-form discovery
│ derivation + ChannelRelay/HubLegTemplate — review 008/ADR-051)
+587
View File
@@ -0,0 +1,587 @@
# Fuzzing alktunnels: Proposal and Plan
**Status:** adopted — wave-1 complete (`datagram_codec` +
`params_parse` implemented, seeds committed, two 10-min campaigns
clean, two harness-model findings fixed — see §7); wave-2
(`session_pumps`) implemented and campaigning — see §3.3/§7.
Campaigns run via the detached runner (§6) — **never as a foreground
child of an agent session** (§5).
**Date:** 2026-09-28
**Upstream precedent:** `/workspace/@alkdev/alkcall/docs/research/fuzzing.md`
(0.8.0 — 0.8.1 campaign; one real finding: duplicate adopt/open destroying
the live channel, fixed in 0.8.1). Tooling, corpus policy, runner, and the
no-hosted-CI posture are inherited from that doc wholesale — this doc does
not re-litigate them; it scopes them to this crate.
---
## 1. Should this crate be fuzzed?
**Yes — but scoped smaller than alkcall's.** The decision follows from
three lines, all pointing the same way:
1. **alkcall's fuzz doc already assigned this to us.** Its §5
"non-surfaces (deliberately)" note ends: *the channels layer carries
opaque `Bytes` and the handler owns sub-stream framing (ADR-035) —
those downstream parsers are downstream crates' fuzz targets.* Our
`wire.rs` datagram codec IS that named downstream target. Fuzzing it
completes alkcall's fuzz scope rather than duplicating it.
2. **We own one hand-rolled parse surface of exactly the covered class.**
`DatagramReader::feed` (`src/wire.rs:100`) is an incremental
length-prefix state machine reassembling `[len: u16 BE][payload]`
frames across arbitrary chunk boundaries — length arithmetic and
boundary handling, the dominant rust-fuzz trophy-case classes. It is
sync, pure, allocation-light, and infallible **by contract** (the
decode direction cannot fail; oversize is rejected at frame time) —
a fuzz target is how that contract is *demonstrated* rather than
believed.
3. **The stateful DoS class applies to us directly.** One pre-fuzz
code-review candidate, the analog of alkcall §6.2-1 (the
early-arrival key-space): `DatagramHalves.pending`
(`src/consumer.rs:78`) queues every datagram decoded from a chunk
beyond the one returned to the caller. A single 16 MiB chunk of
`[00 00]` pairs = ~8.4 M empty datagrams → ~8.4 M `VecDeque`
entries (≈ 400+ MiB) from ONE peer chunk — a ~25–50× memory
amplification riding a bounded channel buffer. Example-based tests
structurally cannot find this; a byte-sequence target with a
pending-bytes assertion will.
**The honest caveat** (mirroring alkcall §1): the highest-value alkcall
targets — envelope framing, chunk header, demux, manager — are upstream
and now fuzzed (0.8.1). Our params parsing is serde + registry-side
jsonschema (upstream's `spec_parse` target covers attacker-shaped
schemas compiling at registration). Expected yield here: datagram-codec
pathologies, the pending-queue bound (§1.3), serde/untagged-enum edge
cases in params, and lifecycle invariants in the two-pump shape. That
is worthwhile, not five-targets-worth of new machinery on day one —
hence the two-wave scope in §4. If the first campaigns find nothing,
that is the successful-negative-result posture alkcall's §1 codified:
"we think the codec is robust" becomes a demonstrated property, and the
targets become regression protection for a wire-stable format
(ADR-003).
## 2. Toolchain (inherited, not re-decided)
Identical to alkcall §4: **cargo-fuzz 0.13.2 + `arbitrary` 1.4.x**,
nightly confined to the `fuzz/` subtree via `fuzz/rust-toolchain.toml`,
the crate itself stays stable at MSRV 1.88. AFL optional/deferred;
bolero deferred (same async-friction argument); libafl/honggfuzz/OSS-Fuzz
not adopted. Sanitizer: `--sanitizer=address` with debug assertions
(cargo-fuzz defaults) — no MSAN (no `unsafe` in this crate).
**Layout (the alkcall pattern, load-bearing):**
```
fuzz/
├── Cargo.toml alktunnels-fuzz (nightly-only bins; own [workspace])
├── rust-toolchain.toml pins nightly for this subtree only
├── fuzz_targets/ thin fuzz_target! wrappers (3-line each)
│ ├── datagram_codec.rs
│ ├── params_parse.rs
│ └── session_pumps.rs
├── shared/ alktunnels-fuzz-shared — STABLE-toolchain library
│ └── src/… invariant logic + corpus replay (§3)
├── corpus/<target>/ committed hand-made seeds (§3)
├── gen_fuzz_seeds.py deterministic seed generator (quiche pattern)
├── run-detached.sh §6 detached runner (CWD-independent)
└── README.md
```
- **Invariant logic lives in `fuzz/shared/`**, not inline in the target
binaries — the stable-toolchain corpus replay
(`cargo test --manifest-path fuzz/shared/Cargo.toml`) runs the SAME
invariant functions the fuzzer runs, on stable, in the release
verification checklist. This is CI tier-3's deliverable (alkcall
§7.2) minus automation; per alkcall §7.9 this repo has no hosted CI
and will not get one (same gitea posture) — corpus replay as plain
`cargo test` is the fuzz gate.
- **Root `Cargo.toml` needs the explicit `[workspace]` table**
(`members = ["."]`, `exclude = ["fuzz"]`) and `"fuzz/"` on the
publish exclude list — without it cargo auto-discovers `fuzz/shared/`
into the main workspace and the MSRV toolchain tries to build the
nightly-consumed dev-deps (the exact failure alkcall hit).
- **Corpus policy:** commit hand-made seeds, gitignore grown corpora
(quinn/h2 model). Seeds per target: valid frames, truncation at every
short prefix length, `len = 0` / `len = MAX` / `len = MAX+1`, empty
datagrams, batching (two frames per chunk), splitting (one frame per
many chunks), and for params: every substrate value, unknown
substrate, unknown/missing fields, `listen: 0`, malformed reply
shapes, untagged cross-shape targets, deep/invalid JSON.
## 3. Invariants by target
### 3.1 `datagram_codec` (wave 1)
Input: raw `&[u8]` (and in the shared logic, chunk-slice parameter
arbitrary). Drives `DatagramReader::feed` + `frame_datagram`
(`src/wire.rs`). Invariants:
- **no-panic on any byte sequence** (the baseline; the one
`unreachable!` in `feed` is a structural invariant of the state
machine — the target is how it stays true).
- **infallibility**: `feed` never errors (contract — oversize is
rejected at frame time; the decode direction is infallible by
design, `src/wire.rs` docs).
- **datagram-size bound**: every yielded datagram is
`<= MAX_DATAGRAM_LEN` (u16 frame field makes this structural — the
target pins it against future refactors).
- **no partial yields**: a datagram completes only when exactly
`declared` payload bytes are buffered (`is_mid_datagram` observable
consistency); `feed(&[])` never yields anything and never clears
mid-datagram state.
- **round-trip**: `feed(frame_datagram(x)) == [x]` for any x; the
reverse direction `write(feed-parse(x))` re-frames to the same
frame for any well-framed input.
- **F-2 sentinel layering**: `len == 0` is a legal empty datagram,
never EOF; the codec never emits zero-length datagrams for non-empty
frames and never treats an empty datagram as stream end.
- **consumption accounting**: re-framing all consumed bytes and
feeding the byte-level remainder yields the same datagram stream
(splitting a feed at any boundary is transparent).
- **oversize frame rejection**: `frame_datagram` errors iff payload >
`MAX_DATAGRAM_LEN`, echoing the sizes.
- **pending-queue bound (the §1.3 finding-class assertion)**: total
bytes retained across `buf` + `current` + `pending` after a feed is
bounded by the fed chunk size plus the one in-flight frame —
an adversarial peer chunk cannot force unbounded queue growth in
the decoder state. (NOTE: this is the *decoder-state* bound; the
consumer-side `DatagramHalves.pending` backlog between chunk-read
and `recv_datagram` pulls is a session-shape question tracked in
§4/§8 — the wave-2 stateful target, or a code fix if the bound is
judged unacceptable. The fuzzer documents the amplification factor;
the remediation decision is ours. **Wave-2 resolution (§3.3):** the
session-shape backlog is now an asserted invariant — the harness's
per-op drain holds the pending queue to the routed-batch remainder
(≤ one chunk's worth of batched datagrams, drained by
`recv_datagram` pulls), so the amplification question is bounded
by construction + assertion rather than remediated; the
`datagram_backlog()` diagnostic exposes the live count.)
### 3.2 `params_parse` (wave 1)
Input: raw bytes → `serde_json` (with alkcall's bounded
`arbitrary_value` approach — depth/width-capped `Value` generation) →
`TunnelParams` / `TunnelDirectParams` / `TunnelForwardedParams` /
`SubstrateAddr` deserialization + `parse_listen_reply` /
`listen_reply_fields` (`src/params.rs`). Invariants:
- **no-panic on any JSON-shaped input** (untagged-enum serde dispatch,
`deny_unknown_fields`, the `positive_u32` pin).
- **clean failure only**: parse failures are `Err`, never panics;
`parse_listen_reply` returns `TunnelError::MalformedReply` on
malformed shapes.
- **round-trip for valid params**: `from_json(to_json(p)) == p` for
constructed params of every substrate.
- **deny-unknown posture**: unknown top-level fields reject;
untagged-variant leniency (extra fields inside a `SubstrateAddr`
variant) is the documented asymmetry — asserted, not "fixed".
- **the `listen: 0` pin**: zero rejects at serde level.
- **projection agreement**: `listen_reply_fields` yields both-`Some`
iff `parse_listen_reply` is `Ok`; it never panics and never refuses
construction (the lenient view projects malformed to both-`None`).
- **establishment-reason surface**: `establishment_reason` never
panics on arbitrary `ChannelOpenError` shapes.
### 3.3 `session_pumps` (wave 2)
Input: op sequences (`#[derive(Arbitrary)]` `SessionSequence` — the
`manager_routing` pattern) over one live channels stack per exec:
MuxRunner → raw chunk stream → the real `run_demux_loop_for_client`
demux → `ChannelManager` per side (producer Connect / consumer
Accept over one duplex transport — the reverse POC's topology). Ops:
`Route` (producer→consumer framed datagrams / raw stream bytes via
`route_payload`), `AdoptDatagram` / `AdoptStream` (the
`TunnelSession` data-plane mirrors — codec reader + `pending`, and
`pump_bidi` against in-process peer halves), `Pump` (peer→channel;
`len == 0` drops the p2c source — the EOF path), `Teardown`,
`ClearAll`. Invariants:
- **no-panic / no-wedge on any op sequence** (the baseline; the
harness's 5 s reconcile bound turns a wedged pump into a finding,
and §5's `-timeout=25` fork containment is the outer net).
- **two-pump shutdown-on-completion (convention 8)**: after the p2c
source drops, the pump's channel-side shutdown must propagate and
the pump must complete once the teardown EOFs the c2p source —
pinned by the `reconcile` join with the 5 s bound.
- **lossless p2c copy**: the pump's `p2c` count equals exactly what
the harness pushed into the peer half (copy never truncates).
- **EOF-sentinel teardown semantics**: after a sentinel (teardown /
`clear_all`), the reassembled stream drains its queue before
reporting EOF — every routed datagram consumable, nothing lost;
the consumer map empties; the codec's `len == 0` sentinel layering
holds at the session layer (a routed empty datagram is data; the
channels EOF is a separate mechanism).
- **FIFO reconciliation**: every complete frame routed through the
real demux path arrives as exactly one datagram of the declared
length, in order (the model walks the framing as the codec does —
the wave-1 F-2 walk, reused at the session layer).
- **pending-queue bound (the §1.3 finding-class assertion)**: the
session-shape `DatagramHalves.pending` backlog never exceeds the
unrouted remainder — i.e. the backlog holds only datagrams decoded
from routed chunks beyond the ones consumed; with the harness's
per-op drain the queue returns to zero after every op, so an
amplification factor > 1× chunk size would trip the assert. (The
wave-1 decoder-state bound re-asserted per op alongside.)
- **monotonicity**: the manager's early-arrival / dropped counters
survive `clear_all` (the alkcall §7.7 pattern), and the consumer
map empties on the transport-EOF path.
Bounds: 128 ops, ≤ 512-byte payloads (generator-clamped; the codec's
`MAX_DATAGRAM_LEN` stays the wire bound). Even-id masking (ADR-047
§5 — the consumer is the Accept side; the producer allocates even).
## 4. Scope: two waves
**Wave 1 (complete):** the two targets above. Both drive already-
`pub` APIs — no `#[cfg(fuzzing)]` exposure needed (matching alkcall's
experience where none of five targets needed it).
**Wave 2 (this session):** `session_pumps`, the stateful op-sequence
target over the two-pump tunnel shape in the alkcall `manager_routing`
style (`#[derive(Arbitrary)]` op enum; live mux + demux runners; the
alkcall harness lessons applied: even-id masking, drained-state
asserts). Drives: the `TunnelSession` data-plane shapes (the datagram
codec plane + the `pump_bidi` stream plane) over a REAL channels
stack — the two-pump shutdown-on-completion contract (AGENTS.md
convention 8), the EOF-sentinel teardown semantics, and the §1.3
backlog bound as byte-accounting invariants (§3.3). The
`forwarded_routing` correlation target stays deferred until the
assembly-side correlation registry (ADR-008 §3, assembly
bookkeeping) grows into this crate.
Sequencing mirrors alkcall §7.4: init + wave-1 targets → 10–30 min
campaigns per target via the detached runner → fix anything found →
wave 2 (`session_pumps`) → further targets when justified.
## 5. Operational isolation: campaigns never share fate with the agent session
**This is an environment constraint, not a nice-to-have** (upstream
doc §7.6, adopted verbatim as policy here). Agent sessions (opencode)
run fuzzer builds/campaigns through a bash tool that spawns the fuzzer
as a **child of the session's own process tree and cgroup**. A fuzz
run is precisely the workload shape that kills its own host:
- **OOM-killer shared fate**: the target's worst case IS unbounded
allocation. If libFuzzer's soft RSS guard loses the race with the
kernel, the OOM killer fires — and it picks the largest-RSS process
in the cgroup, which can be the opencode server hosting the session.
That is *recoverable* (sessions are recorded in a DB) but hugely
derailing — the session's live environment access dies mid-task.
An OOM in a fuzz target must cost the fuzzer, never the session.
- **Fork bombs / CPU saturation**: `-fork=N` workers; a runaway
campaign starves the session host.
- **Wedged targets**: a deadlock in an async harness would hang a
foreground bash call forever.
Three layers of defense, cheapest first — **all mandatory**:
1. **libFuzzer soft limits (always on)**: `-rss_limit_mb=2048`
`-malloc_limit_mb=2048` make libFuzzer exit cleanly (artifact
recorded) before the kernel gets involved. Note the RSS limit is
*soft* (polls `/proc/self/statm`); a fast huge allocation can beat
it — that is what layers 2–3 are for. Never `ulimit -v` with ASAN
(it reserves terabytes of virtual address space); the ASAN-safe
hard-limit recipe is `-fork=1`.
2. **Fork mode as blast-radius containment (default)**: `-fork=1`
(or a small number) runs each input in a short-lived child — a
crash/timeout/OOM/leak kills only that child; the harness survives,
records the artifact, keeps fuzzing. Also the deadlock answer: the
per-input timeout kills the child, not the session's bash call.
3. **Process detachment from the session tree (MANDATORY for agent-run
campaigns)**: never run fuzzing as a foreground child of the
session. The runner script wraps `cargo fuzz run` in `setsid` +
`nohup` + full redirection to a log file: (a) removes the fuzzer
from the session's controlling terminal and signal relation, (b)
survives the session ending, (c) gives the agent a pollable log +
artifact directory instead of a blocking call. Combined with fork
mode, an OOM-class finding costs one child process.
**The runner** (`fuzz/run-detached.sh`, CWD-independent — resolves its
own repo root):
```bash
#!/usr/bin/env bash
# Detached fuzzing runner for agent sessions: the campaign never runs
# as a foreground child of the session (an OOM in a target must cost
# the fuzzer, never the agent host), and survives the session ending.
set -euo pipefail
target="${1:?usage: run-detached.sh <target> [extra libfuzzer args...]}"
shift
runtime="${FUZZ_RUNTIME_SECS:-600}"
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
log_dir="$root/fuzz/artifacts"
mkdir -p "$log_dir"
log="$log_dir/${target}-$(date -u +%Y%m%d-%H%M%S).log"
cd "$root"
setsid nohup cargo fuzz run "$target" -- \
-fork=1 -rss_limit_mb=2048 -malloc_limit_mb=2048 -timeout=25 \
-max_total_time="$runtime" "$@" \
>"$log" 2>&1 < /dev/null &
echo "pid=$! log=$log"
```
**Agent polling protocol** (never `wait` on the detached process from a
tool call — poll instead):
- `tail -n 50 fuzz/artifacts/<target>-*.log` — campaign progress.
- `ls fuzz/artifacts/` — `crash-*` / `oom-*` / `timeout-*` / `leak-*`
artifacts are the findings.
- `pgrep -f "cargo fuzz run <target>"` — still running?
- One detached campaign per target at a time; log filenames carry the
UTC timestamp.
**Flags worth pinning beyond the runner defaults**: `-max_len=65536`
on the codec target; `-use_value_profile=1`; a JSON token `-dict` for
the params target (alkcall's `json.dict` transfers as-is).
## 6. No hosted CI
Per alkcall §7.9 — the standing policy applies to this repo identically:
no hosted CI will exist; do not add Actions/Gitea-Actions workflow
files. **Corpus replay is the fuzz gate as plain `cargo test`**:
`cargo test --manifest-path fuzz/shared/Cargo.toml` replays every
committed seed through the invariant functions — add it to the release
verification checklist (AGENTS.md). Campaigns run manually via the
detached runner (§6) — e.g. before a release, or after touching
`src/wire.rs`, the consumer data planes, or `src/params.rs`.
## 7. Implementation log
- **2026-09-28 — proposal adopted** (this doc). Wave-1 targets
`datagram_codec` + `params_parse`; wave-2 deferred (§4).
- **2026-09-28 — `alkcall` bumped 0.8.0 → 0.8.1.** Bug-fix release, no
API/wire change; the fix (duplicate adopt/open no longer destroying
the live channel) is directly relevant to this crate's
`TunnelSession::adopt` path — the changelog's "open-op response
replayed / id re-announced" scenario is our reverse path. Verification:
full suite (135 passed), clippy, fmt, wasm check (§8 commands).
- **2026-09-28 — wave-1 implemented.** `fuzz/` workspace laid out per
§2 (nightly-pinned subtree, stable `fuzz/shared` library, runner,
dict, generator); `datagram_codec` + `params_parse` targets over
already-`pub` APIs — no `#[cfg(fuzzing)]` exposure needed. One new
public diagnostic: `DatagramReader::retained_bytes()` (the
decoder-state byte count the §3.1 bound asserts against, in the
`is_mid_datagram` diagnostic style). Seeds committed: 27 +
69 generated (fuzz/gen_fuzz_seeds.py) + 1 campaign-found crash
input promoted to `datagram_codec/seed-900` (see the finding below).
Stable side: corpus replay green, shared crate clippy/fmt clean;
main suite untouched and green; `cargo fuzz build` clean.
- **2026-09-28 — first campaign finding: a HARNESS-model bug, not a
crate bug.** The `datagram_codec` campaign crashed in its first
minute on input `00 2b 01 00 00 05` (`fuzz/artifacts/datagram_codec/
crash-e9ba284e…`, exit 77, fork-mode containment worked as
designed — the harness parent survived and persisted the artifact).
Analysis: the codec is CORRECT — `00 2b` declares a 43-byte
datagram; `01 00 00 05` is its in-flight payload; nothing is
yielded. The invariant that tripped ("an empty datagram must come
only from a [00 00] length header") was mis-modeled: it counted
`00 00` byte pairs anywhere in the RAW stream, but a `00 00` pair
sitting INSIDE a frame's payload region is payload, not a length
header — exactly the same class as alkcall §7.7's probe (the
harness claimed `4 + len` when the prefix counts only the body).
Fix: the count now walks the framing as the decoder does (complete
frames from the start; the walk stops where the decoder would be
stuck mid-frame). The crash input is committed as seed-900 and
replays clean; the campaign relaunched clean.
- **2026-09-28 — second harness finding (caught by corpus replay,
pre-campaign): `establishment_reason` is a pass-through extractor,
not a validated table.** The params.rs doc names the pinned reason
strings, but the upstream implementation (alkcall
`channels/client.rs` `establishment_reason`) extracts
`details.reason` AS-IS whenever the code is `channel:open_failed` —
a made-up reason is surfaced as-is by design, and validation is the
consumer's job. The harness first asserted a closed set of reason
strings and corpus replay tripped it on `made_up_reason` before any
campaign ran. Invariant corrected to assert the extraction shape
(Some iff code == `channel:open_failed` and reason is a string).
- **2026-09-28 — second harness finding (same replay pass):
`is_mid_datagram` means a partial DATAGRAM, not buffered header
bytes.** A dangling 1-byte length prefix at input end is buffered
header state (no frame in flight, `is_mid_datagram == false`);
the harness first asserted `true` for that case. Corrected:
mid-datagram iff the framing walk leaves a remainder ≥ 2 bytes
(a parsed-but-unfinished payload), not `!= 0`.
- **2026-09-28 — wave-1 campaigns run (10-min detached runs, §6 runner;
`-fork=1 -rss_limit_mb=2048 -malloc_limit_mb=2048 -timeout=25`;
codec with `-max_len=65536`, params with `-dict=json.dict
-use_value_profile=1`). Both exited 0.**
- `params_parse` — 3.14M execs (~5.3k exec/s), 33 fork jobs,
`oom/timeout/crash: 0/0/0` throughout. Coverage 2049 edges /
15.6k features; in-memory corpus 69 committed seeds → 3018
entries — the serde/untagged-enum shape space was still expanding
at budget end, so longer campaigns keep paying here.
- `datagram_codec` (post-harness-fix) — 827k execs, 33 fork jobs,
`oom/timeout/crash: 0/0/0` throughout. Coverage saturated at 221
edges (ft 1198) — the 2-byte-header state machine is fully
enumerated, matching alkcall §7.7's `chunk_header` saturation
profile. Exec/s reads low (~400) because each exec replays the
input through a ~35-feed 3-byte-sliced sequence alongside the
whole-input feed; the state space is small and exhausted early.
- The pre-fix `datagram_codec` run (the §7 finding above) crashed at
job 4 and exited 77; fork-mode containment behaved as designed —
the harness survived, persisted the artifact, and the run wrapped
up. That artifact is the promoted seed-900.
- Grown corpora (3018 params entries) were **not** merged into the
committed seeds — per the §2 policy, grown corpora stay
gitignored.
- Operational note for future sessions: `cargo fuzz run` returns
before its last fork-mode children fully exit; a `pkill -f
"cargo fuzz run <target>"` followed immediately by another
`pgrep` of the same pattern can wedge a bash tool call on its own
process match — prefer `pgrep -af` to LIST, kill by explicit pid
from the runner's echoed `pid=`, and never pattern-kill-and-recheck
in one call.
- **NO crate bugs found by wave 1.** The two findings this session
were both harness-model defects (§7 above), each invisible to
example-based review and caught by fuzzing/replay — the alkcall §7.7
"fuzzer as a harness-checker" pattern, confirmed. The §1.3
decoder-state bound held everywhere: `retained_bytes()` never
exceeded fed + one frame on any adversarial input. The session-shape
`DatagramHalves.pending` amplification remains a documented
§1.3/§4 note (wave-2's motivation), not a campaign finding — the
wave-1 targets assert the decoder, not the session pump.
- **2026-09-28 — wave-2 implemented: `session_pumps` (the stateful
two-pump/EOF-sentinel target, §3.3).** The harness mirrors
`TunnelSession`'s data-plane shapes over one REAL channels stack
per exec (MuxRunner → raw chunk stream → the real
`run_demux_loop_for_client` demux → `ChannelManager` per side over
one duplex transport; producer Connect / consumer Accept — the
reverse POC's topology). Ops: Route/AdoptDatagram/AdoptStream/
Pump/Teardown/ClearAll; invariants per §3.3. One diagnostic
addition to the crate: `TunnelSession::datagram_backlog()` (the
`DatagramHalves.pending` count the §3.3 bound asserts against, in
the `retained_bytes()` diagnostic style) + a suite test pinning
its rest states. Seeds: 8 committed (deterministic decode-scan
hex patterns, the `manager_routing` precedent — arbitrary ships
no encoder) + 2 in-source semantic fixtures (the §1.3 batched-
backlog probe; the W4 half-close shape). Two harness-model
findings caught by corpus replay pre-campaign:
- **the p2c-EOF pump-join wedge is the DESIGNED half-close
shape, not a wedge.** The first `reconcile` draft awaited the
pump after dropping only the p2c source — but the c2p pump's
source (the channel read half) EOFs only via teardown, so the
join legitimately blocked (the 5 s bound tripped). `pump_bidi`
completes when BOTH pumps finish; the half-close contract is
that the p2c completion shuts the channel side down (the
observable), and the pump completes once the c2p source EOFs
too. The harness now models exactly that: drop p2c source →
teardown the entry → join with the 5 s bound. (The alkcall §7.7
harness-checker pattern again — the fuzzer harness's first
campaign-found crash in wave 1 and this replay catch are both
harness-model defects, not crate bugs.)
- **the stream-plane c2p accounting is asynchronous** — the pump
writes channel→peer into the peer duplex while a far-end reader
task counts; the reconcile join (after the pump completes) is
when the count is final. First draft read it early. Fixed by
reading after the reader task's join.
- **2026-09-28 — third harness finding (campaign-found, fork-mode
containment as designed): duplicate adopt is a LEGAL manager
outcome, not a finding.** The first campaign crashed at ~49 s on
`AdoptDatagram { channel_id: 0 }` after the id had already been
adopted — `ChannelExists` is the manager's documented
id-collision error, and my adopt arms treated every error as a
finding. Fix: both adopt arms model the documented error table
(`ChannelExists` / `TooManyChannels` are no-ops, exactly the
alkcall `manager_routing` harness's treatment). The crash input
is committed as seed-900 and replays clean; the campaign
relaunched clean.
- **2026-09-28 — fourth harness finding (second campaign): the
harness modeled the producer→consumer path through the WRONG
primitive.** The second campaign crashed at ~5 s on `Route`:
raw-id addressing (`even_id` masked arbitrary ids; collisions +
an `UnknownChannel`-class state the model didn't track), `len ==
0` raw routes doubling as EOF sentinels mid-model, and — the
structural one — `route_payload` was used as the producer's
forward write. `route_payload` routes into the LOCAL manager's
channel entry (the demux direction); the producer pump handler
writes the channel's SEND half (the mux pump frames onto the
transport). Bytes never reached the consumer. Rewrite (the real
protocol shape, matching `manager_routing`'s op-vocabulary
lesson): producer `Open` (`open_channel` allocates the id, the
harness holds the send half — the pump handler's write side),
session-INDEX addressing (no masking, no collisions), `Adopt`
consumes the producer-allocated id (the early-arrival drain is
the real adopt-race path), `Route` writes the producer send half
(the real wire path: mux pump → transport → consumer demux →
reassembly), `Pull` (the `recv_datagram` pull pops the backlog +
the model), and a STATEFUL framing model (partial frames persist
across ops — the wave-1 walk carried across ops). EOF sentinels
now arise only from teardown/sender-drop (the real mechanisms),
never from `len == 0` routes (F-2: a routed empty datagram is
DATA). All committed session_pumps seeds regenerated by the
rewrite (the old hex patterns decode against the new op enum);
the semantic fixtures re-pinned. Verification of the fix: the
campaign's 0-exec crash went away; batched frames now reconcile
FIFO through the real two-manager stack.
- **2026-09-28 — fifth + sixth harness findings (third campaign's
first minutes): the two ClearAll modeling defects.** (5) A
double-adopt of a live session panicked the harness's own guard —
but a re-adopt of a LIVE id is the manager's documented
`ChannelExists` outcome (a legal no-op; alkcall 0.8.1's
insert-replace fix leaves the live entry intact). Modeled as a
no-op. (6) A `ClearAll` followed by `Route` on the same session
tripped the backlog reconciliation — the harness retired only the
sessions whose ids appeared in `clear_all`'s RETURN value, but
that value is the OPENER-LEDGER records (ADR-047 §7): adopted
channels carry no ledger entry, so the set is empty while the map
itself empties. The alkcall §7.7 lesson ("`drained.len() ==
open_count()` is not an invariant"), learned again at this
crate's layer — the map-empties assert is the ground truth; the
harness now retires EVERY session on `ClearAll`.
- **2026-09-28 — wave-2 campaign run (10-min detached run, §6
runner; `-fork=1 -rss_limit_mb=2048 -malloc_limit_mb=2048
-timeout=25 -max_len=4096 -use_value_profile=1`): exited 0.**
228,676 execs (~380 exec/s; each exec replays up to 128 ops on a
live two-manager stack with real mux/demux tasks), 32 fork jobs,
`oom/timeout/crash: 0/0/0` throughout. Coverage 3427 edges /
17,529 features / 1184 in-memory corpus entries and STILL
EXPANDING at budget end (the op-interleaving space is much larger
than the parser targets') — longer campaigns keep paying. The
two-pump contract held across every adversarial sequence: no
wedged pump (the 5 s reconcile join), no lost datagram (the
FIFO/backlog reconciliation), no premature EOF (the sentinel
layering). The §1.3 backlog bound held everywhere: the
`DatagramHalves.pending` queue never exceeded the
routed-but-unpulled remainder.
- **NO crate bugs found by wave 2.** All six findings across waves
1–2 were harness-model defects (four this session), each
invisible to example-based review and caught by fuzzing/replay —
the alkcall §7.7 "fuzzer as a harness-checker" pattern,
re-confirmed. The stateful thesis is validated from the harness
side: the harness defects themselves (double-adopt semantics, the
clear_all ledger/map distinction, the route_payload direction)
are exactly the interleaving-shape mistakes the alkcall §7.8
campaigns caught in their own drivers.
- (progress appended as implementation proceeds — see git log)
## 8. Verification commands
```bash
cargo test # main suite (stable)
cargo test --manifest-path fuzz/shared/Cargo.toml # corpus replay (fuzz gate)
cargo fuzz build # in fuzz/ (nightly, ASAN)
fuzz/run-detached.sh <target> # campaigns (never foreground)
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo clippy --manifest-path fuzz/shared/Cargo.toml --all-targets -- -D warnings
cargo fmt --manifest-path fuzz/shared/Cargo.toml --check
cargo check --target wasm32-unknown-unknown # default crate stays wasm-clean
```
Campaign flags per target: `datagram_codec -max_len=65536`;
`params_parse -dict=json.dict -use_value_profile=1`;
`session_pumps -max_len=4096 -use_value_profile=1`.
## 9. References
- Upstream campaign + operational model (authoritative for tooling,
runner, corpus policy, no-CI posture):
`/workspace/@alkdev/alkcall/docs/research/fuzzing.md`
- alkcall 0.8.1 changelog — the finding our adopt path is exposed to
- This crate: ADR-003 (codec framing), ADR-002 (ALPN), ADR-007/008
(direct/forwarded ops), `docs/architecture/wire.md`
- External: RUSTSEC-2026-0037 (quinn-proto — the maintainers'
fuzzing-coverage statement), rust-fuzz trophy case, cargo-fuzz Rust
Fuzz Book (all linked from the upstream doc's §9)
+10
View File
@@ -0,0 +1,10 @@
target
corpus/*
!corpus/datagram_codec
!corpus/params_parse
!corpus/session_pumps
# Grown corpus entries (libFuzzer hash-named) are gitignored; only the
# hand-made seed-* files are committed (the quinn/h2 policy).
corpus/*/[0-9a-f]*
artifacts
coverage
+1297
View File
File diff suppressed because it is too large. Load diff
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "alktunnels-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
alktunnels-fuzz-shared = { path = "shared" }
[dependencies.alktunnels]
path = ".."
[[bin]]
name = "datagram_codec"
path = "fuzz_targets/datagram_codec.rs"
test = false
doc = false
bench = false
[[bin]]
name = "params_parse"
path = "fuzz_targets/params_parse.rs"
test = false
doc = false
bench = false
[[bin]]
name = "session_pumps"
path = "fuzz_targets/session_pumps.rs"
test = false
doc = false
bench = false
[workspace]
+58
View File
@@ -0,0 +1,58 @@
# alktunnels fuzzing
libFuzzer targets for alktunnels' untrusted-input surfaces (see
`docs/research/fuzzing.md` for the full rationale, scope decision, and
campaign plan; the tooling/corpus/runner/no-CI posture is inherited
from alkcall's `docs/research/fuzzing.md`).
## 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 (fuzzing.md §5).
- `json.dict` — JSON token dictionary for the params target
(transferred from alkcall).
## Targets
| Target | Drives | Invariants |
|---|---|---|
| `datagram_codec` | `DatagramReader::feed` / `frame_datagram` (`src/wire.rs`) | no-panic; infallible decode; yielded datagrams ≤ `MAX_DATAGRAM_LEN`; F-2 sentinel layering (empty datagram ≠ EOF, only from `[00 00]` headers); splitting transparency (3-byte slices == whole-input feed); re-frame round-trip; oversize rejection exactly at the bound; `is_mid_datagram` tracks framing; decoder-state retained-bytes bound |
| `params_parse` | `TunnelParams` / `TunnelDirectParams` / `TunnelForwardedParams` / `SubstrateAddr` serde parses + `parse_listen_reply` / `listen_reply_fields` / `establishment_reason` (`src/params.rs`) | no-panic on raw bytes and structured Values; clean typed failure only; round-trip for valid params; `listen: 0` pin; loud/lenient projection-pair agreement; pinned establishment-reason table |
| `session_pumps` | op sequences (`SessionSequence`) over one live channels stack per exec (MuxRunner → real demux → `ChannelManager` per side; the `TunnelSession` data-plane mirrors — codec plane + `pump_bidi` stream planes) | no-panic / no-wedge (5 s reconcile bound); two-pump shutdown-on-completion; lossless p2c copy count; EOF-sentinel teardown semantics (drain-before-EOF, map empties); FIFO datagram reconciliation (framing-walk model); `pending` backlog ≤ routed-batch remainder (the §1.3 bound); decoder-state retained-bytes bound re-asserted per op; monotonic counters |
## 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 params_parse -dict=json.dict -use_value_profile=1
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh session_pumps -max_len=4096 -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.
+1
View File
@@ -0,0 +1 @@
��bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbLine truncated
View File
Whitespace-only changes.
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 @@
��
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.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
4
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{}
+1
View File
@@ -0,0 +1 @@
{"resource": "postgres-primary", "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": "postgres-primary", "substrate": "udp"}
+1
View File
@@ -0,0 +1 @@
{"resource": "sock", "substrate": "unix"}
+1
View File
@@ -0,0 +1 @@
{"resource": "r", "substrate": "tcp", "extra": true}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": "r"}
+1
View File
@@ -0,0 +1 @@
{"resource": "", "substrate": ""}
+1
View File
@@ -0,0 +1 @@
{"resource": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": null, "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": 1, "substrate": 2}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {"host": "db.internal", "port": 5432}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "udp", "target": {"host": "10.0.0.7", "port": 53}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "unix", "target": {"path": "/run/x.sock"}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {"port": 1}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {"host": "h"}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "unix", "target": {"path": "/p", "host": "h"}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {"host": "h", "port": 65536}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {"host": "h", "port": -1}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": [1, 2]}
+1
View File
@@ -0,0 +1 @@
{"substrate": "unix", "target": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "target": {}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"target": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"listen": 918273, "substrate": "tcp", "peer": {"host": "198.51.100.4", "port": 55231}}
+1
View File
@@ -0,0 +1 @@
{"listen": 918273, "substrate": "unix", "peer": {"path": "/run/x.sock"}}
+1
View File
@@ -0,0 +1 @@
{"listen": 0, "substrate": "tcp", "peer": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"listen": -1, "substrate": "tcp", "peer": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"listen": 1, "substrate": "tcp", "peer": {"host": "h", "port": 1}, "channel": 7}
+1
View File
@@ -0,0 +1 @@
{"listen": 4294967296, "substrate": "tcp", "peer": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"listen": "token", "substrate": "tcp", "peer": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"substrate": "tcp", "peer": {"host": "h", "port": 1}}
+1
View File
@@ -0,0 +1 @@
{"listen": 1, "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"listen": 1, "substrate": "unix", "peer": {"path": "/p", "host": "h"}}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "bound": {"host": "203.0.113.9", "port": 42113}, "listen": 918273}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 0}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "bound": {"path": "/run/x.sock"}, "listen": 5, "future_field": {"n": true}}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "bound": {"port": 1}}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "listen": 0}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "listen": "token"}
+1
View File
@@ -0,0 +1 @@
{"bound": {"path": "/p"}}
+1
View File
@@ -0,0 +1 @@
{"channel_id": -7}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7.5}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "bound": 5}
+1
View File
@@ -0,0 +1 @@
{"channel_id": 7, "listen": 4294967296}
+1
View File
@@ -0,0 +1 @@
{}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "message": "establishment failed", "details": {"reason": "dial_failed"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "unknown_resource"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "resource_shortage"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "handler_error"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "timeout"}}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:open_failed", "details": {"reason": "made_up_reason"}}
+1
View File
@@ -0,0 +1 @@
{"code": "forbidden"}
+1
View File
@@ -0,0 +1 @@
{"code": "channel:too_many_channels", "details": {"reason": "dial_failed"}}
+1
View File
@@ -0,0 +1 @@
{"details": null}
+1
View File
@@ -0,0 +1 @@
{"resource": ["a" * 1], "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": {"nested": {"deep": [1, 2, {"x": [1, [2, [3]]]}]}}, "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": "ÿþ", "substrate": "tcp"}
+1
View File
@@ -0,0 +1 @@
{"resource": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxLine truncated
+1
View File
@@ -0,0 +1 @@
{"substrate": {"enum": "tcp"}}
+1
View File
@@ -0,0 +1 @@
[{"resource": "r", "substrate": "tcp"}]
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more