From 3bda29c419baf5d811fd8137b1144c8b2b2a0048 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Mon, 28 Sep 2026 06:56:11 +0000 Subject: [PATCH] =?UTF-8?q?chore(release):=200.8.1=20=E2=80=94=20duplicate?= =?UTF-8?q?=20adopt/open=20fix=20+=20fuzz=20harness=20bookkeeping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - version 0.8.0 -> 0.8.1 (bug-fix release; semver-checks 196 pass vs 0.8.0) - CHANGELOG 0.8.1: the manager_routing-found duplicate adopt/open fix, the fuzz harness, and the verification summary - publish exclude gains docs/research/ (internal research notes; fuzz/ was already excluded — package verified clean of both) - fuzzing.md §7.9: standing no-hosted-CI policy — corpus replay is the release-verification fuzz gate; campaigns manual via the detached runner; OSS-Fuzz out; no workflow files in this repo - AGENTS.md: corpus replay added to the verification checklist - lockfiles: alkcall 0.8.1 (root + fuzz) Verification: 684 tests; clippy -D warnings (main, wasm, fuzz/shared); fmt clean; doc clean; wasm32-unknown-unknown check+clippy clean; semver-checks 196 pass; publish --dry-run 116 files (no fuzz/research/ reviews/sdd/AGENTS in the tarball); fuzz corpus replay 5/5 green --- AGENTS.md | 1 + CHANGELOG.md | 60 ++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 4 +-- docs/research/fuzzing.md | 48 ++++++++++++++++++++++++++++---- fuzz/Cargo.lock | 2 +- 6 files changed, 107 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4f3c677..ccd1ff5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -194,6 +194,7 @@ Run these before committing. All must pass. ```bash cargo test # full suite +cargo test --manifest-path fuzz/shared/Cargo.toml # fuzz corpus replay (stable) cargo clippy --all-targets -- -D warnings cargo fmt --check cargo doc --no-deps # if docs changed diff --git a/CHANGELOG.md b/CHANGELOG.md index 8972601..9cf2533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,65 @@ All notable changes to this crate are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this crate adheres to [Semantic Versioning](https://semver.org/). +## [0.8.1] - 2026-09-28 + +Bug-fix release: a duplicate `adopt_channel`/`open_channel` no longer +destroys the live channel it collided with, found by the crate's new +fuzzing harness (`fuzz/` workspace — five cargo-fuzz targets, committed +seed corpora, three 10-minute campaigns clean). No API or wire-format +change; the error is still `Err(ChannelExists)` — the difference is +entirely in what the collision leaves behind. + +### Fixed + +- **Duplicate adopt/open no longer replaces the live channel's state + (found by the `manager_routing` fuzz target, + `docs/research/fuzzing.md` §7.8).** `ChannelManager::open_channel` + and `adopt_channel` used `HashMap::insert(...).is_some()` as the + collision check — but `insert` *replaces* an occupied entry and + returns the old value, so a duplicate on an in-use id installed the + new `ChannelState`, dropped the live channel's `demux_sender` + (spurious EOF to its readers; all subsequently routed chunks lost), + and *still* returned `Err(ChannelExists)`. The mux write half kept + framing onto the transport for a channel the demux no longer fed. + Both functions now check `contains_key` and return before any map + mutation — the live channel's routing state is untouched on + collision. Reachable whenever an open-op response is replayed or a + connection-owner race re-announces an id. Regression tests: + `adopt_channel_duplicate_id_leaves_live_channel_intact`, + `open_channel_duplicate_id_leaves_live_channel_intact` (both verify + routing survives a rejected duplicate; the first also verifies the + EOF sentinel remains the only EOF source). + +### Added (development) + +- **Fuzzing harness (`fuzz/` workspace, `docs/research/fuzzing.md`).** + Five cargo-fuzz targets over the two attacker-reachable wire formats + and the stateful channel-routing surface: `chunk_header` (8-byte + header no-panic/round-trip/accounting), `envelope_frame` (frame + decode error-shape partition + allocation bound), `manager_routing` + (`Arbitrary` op sequences over `ChannelManager` with exact counter + models and the parked-bytes bound), `envelope_semantic` (all six + event kinds: constructors → serde → framing round-trip), and + `spec_parse` (attacker-shaped op-register schemas compile or reject + cleanly at registration). Invariant logic lives in + `fuzz/shared/` — a stable-toolchain crate replayed as plain + `cargo test`, so the committed corpora stay executable without + nightly. Campaigns (10 min per target, detached runner): ~25M + executions total, zero crashes/hangs/OOMs. The `manager_routing` + campaign found the 0.8.1 fix above within minutes — exactly the + stateful interleaving class example-based tests cannot reach. + `fuzz/` is excluded from the workspace and from the published + package. + +### Verified + +- 684 tests pass; `clippy --all-targets -- -D warnings`, `fmt --check`, + `cargo doc`, wasm32-unknown-unknown check, `cargo semver-checks` + (no API changes vs 0.8.0), and `cargo publish --dry-run` clean; + corpus replay (`cargo test --manifest-path fuzz/shared/Cargo.toml`) + green. + ## [0.8.0] - 2026-09-18 Review 008's remediation lands in full — the graduation upstream asks @@ -678,6 +737,7 @@ Vendored core types (`Connection`, `ProtocolHandler`, `BiStream`, (ADR-046), the channels protocol with openable-ALPNs-as-operations (ADR-047), and the `ChannelClient` transport-agnostic client. +[0.8.1]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.8.1 [0.8.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.8.0 [0.7.1]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.1 [0.7.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.0 diff --git a/Cargo.lock b/Cargo.lock index 34755b4..fa231a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ dependencies = [ [[package]] name = "alkcall" -version = "0.8.0" +version = "0.8.1" dependencies = [ "async-trait", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 81b1b54..25c6288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alkcall" -version = "0.8.0" +version = "0.8.1" edition = "2021" rust-version = "1.88" license = "MIT OR Apache-2.0" @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://git.alk.dev/alkdev/alkcall" keywords = ["rpc", "json-rpc", "multiplexing", "wire-format", "alpn"] categories = ["network-programming", "asynchronous", "encoding"] -exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/sdd_process.md", "fuzz/"] +exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/sdd_process.md", "docs/research/", "fuzz/"] [workspace] members = ["."] diff --git a/docs/research/fuzzing.md b/docs/research/fuzzing.md index 84ab09d..6610039 100644 --- a/docs/research/fuzzing.md +++ b/docs/research/fuzzing.md @@ -3,7 +3,12 @@ **Status:** steps 1–3 of §7.4 adopted — `fuzz/` workspace, five targets, committed seeds, detached runner, two-tier campaigns run; one real finding (duplicate adopt/open destroying the live channel) fixed with -regression tests. CI deferred until a platform exists (§7.8) +regression tests. §7.2/§7.4's CI tiers are **not planned** — this repo +will not run hosted CI (the git host is a minimal gitea that serves git +only; see §7.9). CI's two deliverables are covered instead: the stable +side's corpus replay (`cargo test --manifest-path fuzz/shared/Cargo.toml`) +runs as part of every release's verification checklist (AGENTS.md), and +campaigns run manually via the detached runner (§7.6) **Date:** 2026-09-27 **Research inputs:** web survey of the 2025–2026 Rust fuzzing ecosystem, survey of fuzzing practice in comparable Rust protocol crates (rustls, quinn, quiche, @@ -380,11 +385,15 @@ comparisons), and a `-dict` of JSON tokens for the envelope targets. found; triage §6.2 candidates with targeted corpus entries. **Done for targets 1–2 — see §7.7.** 3. Add targets 3–5, the smoke CI job, and `.gitignore` entries. - **Targets 3–5 done, campaigns clean — see §7.8. CI deferred (no CI - platform exists in this repo yet; the stable-side corpus replay - `cargo test --manifest-path fuzz/shared/Cargo.toml` is the drop-in - smoke gate when a platform is chosen).** -4. Scheduled campaign tier; then OSS-Fuzz application. + **Targets 3–5 done, campaigns clean — see §7.8. The CI job will not + exist — see §7.9 (no hosted CI policy); the stable-side corpus + replay `cargo test --manifest-path fuzz/shared/Cargo.toml` is in + the release verification checklist instead (AGENTS.md).** +4. ~~Scheduled campaign tier; then OSS-Fuzz application.~~ **Not + planned — see §7.9.** Long campaigns are run manually via the + detached runner (§7.6) when wanted; OSS-Fuzz is out (it requires + hosted infrastructure and a public repo posture this project does + not have). ### 7.5 Relationship to existing tests @@ -733,6 +742,33 @@ artifacts directory — no crashes, hangs, OOMs, or leaks.** and held; §6.2-3 confirmed bounded at the parser level (§7.7). The stateful coverage §7.4 step 3 called for exists and is clean. +--- + +## 7.9 No hosted CI — the standing policy (2026-09-28) + +§7.2's two CI tiers (per-push smoke, scheduled campaign) and the +OSS-Fuzz step assume a hosted CI platform. This repo has none and will +not get one: the git host is a minimal gitea that serves git and +nothing else, deliberately — gitea/gitlab have had full-compromise CVEs +(app.ini and any plaintext DB tokens exfiltrated in one real incident +elsewhere), so the attack surface stays minimal. All verification and +campaigns are **manual, run from the separate publishing server**: + +- **Corpus replay is the fuzz gate, as plain `cargo test`:** + `cargo test --manifest-path fuzz/shared/Cargo.toml` replays every + committed seed through the same invariant functions the fuzzer runs + — on stable, no nightly, no cargo-fuzz. It is part of the release + verification checklist (AGENTS.md). This is CI tier 3's deliverable + (§7.2), minus the automation. +- **Campaigns** run via the detached runner (§7.6) when wanted — + e.g. before a release, or after touching `src/protocol/wire.rs`, + `src/channels/wire.rs`, the demux loop, or `ChannelManager`. +- **OSS-Fuzz is out**: it requires hosted infrastructure and a public + repo posture this project does not have. +- Do not add Actions/Gitea-Actions/workflow files anywhere in this + repo, and do not reintroduce "when CI exists" language into docs — + point at this section instead. + ## 8. Answering the "if / how" directly - **If?** Yes — justified by position in the dependency graph, two stable diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c702fbb..874505d 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -27,7 +27,7 @@ dependencies = [ [[package]] name = "alkcall" -version = "0.8.0" +version = "0.8.1" dependencies = [ "async-trait", "bytes",