From c6e704abaa9d87923eafb5b236ca463d75ac205b Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 15:56:36 -0700 Subject: [PATCH 01/10] quest(m1): plan plaintext fuzz crypto and richer sim workloads Follow-ups from the moq-quic fuzz (#5779) and seeded simulation (#5780) PRs. Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/README.md | 2 ++ quest/m1/quic/coverage/fuzz-crypto.md | 25 +++++++++++++++++++++ quest/m1/quic/coverage/sim-workloads.md | 29 +++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 quest/m1/quic/coverage/fuzz-crypto.md create mode 100644 quest/m1/quic/coverage/sim-workloads.md diff --git a/quest/m1/quic/coverage/README.md b/quest/m1/quic/coverage/README.md index a585f2f0565..a34cea6ce5e 100644 --- a/quest/m1/quic/coverage/README.md +++ b/quest/m1/quic/coverage/README.md @@ -47,6 +47,8 @@ and the harness defaults to Cubic while production runs BBR3. - [Characterization tests](/quest/m1/quic/coverage/characterize.md) - golden scheduling order, and every uncovered frame, reset, limit, and endpoint path, on today's harness - [Fuzzing](/quest/m1/quic/coverage/fuzz.md) - a moq-net-style fuzz crate with the upstream parser targets, frame decode, and a connection op-sequence target, run nightly - [Seeded simulation](/quest/m1/quic/coverage/sim.md) - a deterministic, impaired network in `Pair`, BBR3 as the harness default, and property tests over random workloads +- [Plaintext fuzz crypto](/quest/m1/quic/coverage/fuzz-crypto.md) - a fuzz-only plaintext session so the connection target skips TLS and its injected packets decrypt +- [Richer simulation workloads](/quest/m1/quic/coverage/sim-workloads.md) - resets, bidi streams, datagrams, and migration in the random workloads, each with its own invariant ## Related diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md new file mode 100644 index 00000000000..71501afd8c4 --- /dev/null +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -0,0 +1,25 @@ +# [S] Plaintext crypto for fuzzing + +## Goal + +A plaintext crypto session in `moq-quic`'s hidden `fuzz` module (no TLS +handshake, no packet protection) lets the `connection` fuzz target skip the +per-input TLS cost and decrypt the packets it injects, so fuzzing reaches the +frame layer at far higher throughput. It never ships in a non-test build. + +## Plan + +Decided 2026-10-16 with the maintainer: + +- One implementation, in the hidden `fuzz` feature module. The Criterion + benchmarks enable `--features fuzz`, as moq-net's already do, and replace + their bench-local no-op protection (ported from quinn `perf`'s + `noprotection.rs`) with it. +- The `connection` target switches to it, so injected and mutated packets + decrypt instead of being dropped at the AEAD. Today it runs about 64 + inputs/s, almost all spent in the handshake. +- Not a debug transport: nothing outside the `fuzz` feature can name it. + +## Required + +- [Benchmarks](/quest/m1/quic/coverage/bench.md) - lands the no-op protection this replaces diff --git a/quest/m1/quic/coverage/sim-workloads.md b/quest/m1/quic/coverage/sim-workloads.md new file mode 100644 index 00000000000..2d82a22e0c8 --- /dev/null +++ b/quest/m1/quic/coverage/sim-workloads.md @@ -0,0 +1,29 @@ +# [M] Richer simulation workloads + +## Goal + +The seeded simulation's random workloads in `rs/moq-quic/src/tests/sim.rs` +also exercise the paths the QUIC rewrites cut, each with its own invariant, +so a refactor that breaks one fails a replayable seed: + +- **Resets and stops**: RESET_STREAM, STOP_SENDING, and RESET_STREAM_AT + mid-transfer. The reader gets exactly the bytes up to the reliable size, + then the reset code, and stream and connection credit come back. +- **Bidirectional streams**: both sides write on the same streams, and every + byte arrives once and in order in each direction. +- **Datagrams** interleaved with streams: each arrives at most once and + unmodified, and datagrams never stall stream progress. +- **Migration**: NAT rebinding and active migration mid-workload. The + connection survives and every delivery invariant still holds. + +## Plan + +Decided 2026-10-16 with the maintainer. Reuse the workload generator and +invariant checks from the seeded simulation, under the same PR budget (64 +fixed cases) and nightly sweep (`just rs quic-sim`). If migration needs +address changes that `Pair`'s `Link`s cannot express, add the smallest +harness hook for it here; flag it on the PR if that pushes past [M]. + +## Required + +- [Seeded simulation](/quest/m1/quic/coverage/sim.md) - the impaired, deterministic `Pair` and the property tests this extends From d88529569dab55fbd01d980ff4186eac74fbe59b Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 15:59:45 -0700 Subject: [PATCH 02/10] quest(m1): plan the relay burst drill flake Co-Authored-By: Claude Opus 5.5 --- quest/m1/test-flakes-2/README.md | 1 + .../m1/test-flakes-2/relay-burst-impaired.md | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 quest/m1/test-flakes-2/relay-burst-impaired.md diff --git a/quest/m1/test-flakes-2/README.md b/quest/m1/test-flakes-2/README.md index e2fc6db891d..fcbf419a812 100644 --- a/quest/m1/test-flakes-2/README.md +++ b/quest/m1/test-flakes-2/README.md @@ -38,6 +38,7 @@ Public API: none. Wire: none. - [Interop close code](/quest/m1/test-flakes-2/interop-close-code.md) - the browser reliably sees `unauthorized` for a refused session - [Auth refresh mocks time](/quest/m1/test-flakes-2/auth-refresh-mock-time.md) - moq-relay's auth refresh and fixed-lease tests measure a grant's life on one mocked clock - [uring teardown close](/quest/m1/test-flakes-2/uring-teardown-close.md) - moq-uring's published-close teardown test holds under load, fixed at its cause +- [Relay burst drill](/quest/m1/test-flakes-2/relay-burst-impaired.md) - the impaired transport-peer burst drill never aborts its subscription while the link is up ## Related diff --git a/quest/m1/test-flakes-2/relay-burst-impaired.md b/quest/m1/test-flakes-2/relay-burst-impaired.md new file mode 100644 index 00000000000..25c8f670973 --- /dev/null +++ b/quest/m1/test-flakes-2/relay-burst-impaired.md @@ -0,0 +1,25 @@ +# [S] The relay burst drill holds on an impaired peer link + +## Goal + +The `bursts_cross_a_transport_peer::impaired` drill +(`rs/moq-relay/tests/drills.rs`) passes reliably, fixed at its cause. It +still proves that bursts cross a relay peer link over an impaired transport +without the subscription aborting while the link is up. + +## Plan + +Seen 2026-10-16 on the io_uring job for #5772 (run 38974661047, head +1c1055c): the subscription aborted with "internal error" at the drill's +`Err(err) => panic!("the subscription aborted: {err}")` arm, which is +reached only when the route is not flapping. A rerun passed. The PR touched +only the dev shell, so the drill ran only because every crate was retested. + +Decided 2026-10-16 (maintainer): a child of this questline, m1. Reproduce it +first, on the io_uring lane under load. Then find which layer produces the +"internal error": an abort that reaches a subscriber while its peer link is +up is a product bug unless the drill's impairment legitimately cut the link, +in which case the drill must classify that as a flap. Never widen a timeout +or add a retry. + +Public API: none expected. Wire: none. From e3659cc7321ef80fe3cf208115828f857f589fbb Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 16:00:49 -0700 Subject: [PATCH 03/10] quest: STOP_SENDING is observed by the sender, not the reader Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/sim-workloads.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/quest/m1/quic/coverage/sim-workloads.md b/quest/m1/quic/coverage/sim-workloads.md index 2d82a22e0c8..e3da5f6bb50 100644 --- a/quest/m1/quic/coverage/sim-workloads.md +++ b/quest/m1/quic/coverage/sim-workloads.md @@ -6,9 +6,12 @@ The seeded simulation's random workloads in `rs/moq-quic/src/tests/sim.rs` also exercise the paths the QUIC rewrites cut, each with its own invariant, so a refactor that breaks one fails a replayable seed: -- **Resets and stops**: RESET_STREAM, STOP_SENDING, and RESET_STREAM_AT - mid-transfer. The reader gets exactly the bytes up to the reliable size, +- **Resets**: RESET_STREAM and RESET_STREAM_AT mid-transfer. The reader + gets exactly the bytes up to the reliable size (none for RESET_STREAM), then the reset code, and stream and connection credit come back. +- **Stops**: STOP_SENDING mid-transfer. The sender observes the code + through `SendStream::stopped` or `WriteError::Stopped`, the stopped + reader sees nothing further, and credit comes back. - **Bidirectional streams**: both sides write on the same streams, and every byte arrives once and in order in each direction. - **Datagrams** interleaved with streams: each arrives at most once and From ec9de844f61ae8c127e26cbe470e309a48a2a68f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 16:03:35 -0700 Subject: [PATCH 04/10] quest: a plain reset keeps any already-read prefix Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/sim-workloads.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/quest/m1/quic/coverage/sim-workloads.md b/quest/m1/quic/coverage/sim-workloads.md index e3da5f6bb50..db64b13d2b1 100644 --- a/quest/m1/quic/coverage/sim-workloads.md +++ b/quest/m1/quic/coverage/sim-workloads.md @@ -7,8 +7,10 @@ also exercise the paths the QUIC rewrites cut, each with its own invariant, so a refactor that breaks one fails a replayable seed: - **Resets**: RESET_STREAM and RESET_STREAM_AT mid-transfer. The reader - gets exactly the bytes up to the reliable size (none for RESET_STREAM), - then the reset code, and stream and connection credit come back. + sees an unmodified prefix of what was written, then the reset code. The + prefix covers at least the reliable size for RESET_STREAM_AT and may be + any length for RESET_STREAM, since bytes already read stay read. Stream + and connection credit come back. - **Stops**: STOP_SENDING mid-transfer. The sender observes the code through `SendStream::stopped` or `WriteError::Stopped`, the stopped reader sees nothing further, and credit comes back. From 238c2f45fd64f36ed193871c932ce3035adf176d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 16:09:23 -0700 Subject: [PATCH 05/10] quest: lossless sim cases must deliver every datagram Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/sim-workloads.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quest/m1/quic/coverage/sim-workloads.md b/quest/m1/quic/coverage/sim-workloads.md index db64b13d2b1..32d963b81b6 100644 --- a/quest/m1/quic/coverage/sim-workloads.md +++ b/quest/m1/quic/coverage/sim-workloads.md @@ -17,7 +17,9 @@ so a refactor that breaks one fails a replayable seed: - **Bidirectional streams**: both sides write on the same streams, and every byte arrives once and in order in each direction. - **Datagrams** interleaved with streams: each arrives at most once and - unmodified, and datagrams never stall stream progress. + unmodified, and datagrams never stall stream progress. On a lossless link + with buffers sized for the workload, every datagram arrives, so dropping + them all fails. - **Migration**: NAT rebinding and active migration mid-workload. The connection survives and every delivery invariant still holds. From ef4677e9714754e50e70ba78bb718460be35fbe0 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 16:15:27 -0700 Subject: [PATCH 06/10] quest: plaintext fuzz crypto waits on the fuzz target Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/fuzz-crypto.md | 1 + 1 file changed, 1 insertion(+) diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md index 71501afd8c4..6c058808ae2 100644 --- a/quest/m1/quic/coverage/fuzz-crypto.md +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -23,3 +23,4 @@ Decided 2026-10-16 with the maintainer: ## Required - [Benchmarks](/quest/m1/quic/coverage/bench.md) - lands the no-op protection this replaces +- [Fuzzing](/quest/m1/quic/coverage/fuzz.md) - lands the hidden `fuzz` module and the `connection` target this switches From cf8b3cd7c809bb70e5512bbf15fe813df2033441 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 16:25:36 -0700 Subject: [PATCH 07/10] quest: injected stream frames leave the write-integrity oracle Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/fuzz-crypto.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md index 6c058808ae2..2c05eea0916 100644 --- a/quest/m1/quic/coverage/fuzz-crypto.md +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -18,6 +18,9 @@ Decided 2026-10-16 with the maintainer: - The `connection` target switches to it, so injected and mutated packets decrypt instead of being dropped at the AEAD. Today it runs about 64 inputs/s, almost all spent in the handshake. +- A decrypted injected packet can carry a valid STREAM frame, so the + target's delivered-bytes-match-written check excludes the streams injected + frames touch; no-panic still covers every input. - Not a debug transport: nothing outside the `fuzz` feature can name it. ## Required From cad3113e6ddecf5488ef493a45936ff7c847d081 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 17:41:49 -0700 Subject: [PATCH 08/10] quest: drop the landed fuzzing prerequisite Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/fuzz-crypto.md | 1 - 1 file changed, 1 deletion(-) diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md index 2c05eea0916..c6b4be4894e 100644 --- a/quest/m1/quic/coverage/fuzz-crypto.md +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -26,4 +26,3 @@ Decided 2026-10-16 with the maintainer: ## Required - [Benchmarks](/quest/m1/quic/coverage/bench.md) - lands the no-op protection this replaces -- [Fuzzing](/quest/m1/quic/coverage/fuzz.md) - lands the hidden `fuzz` module and the `connection` target this switches From 6dceb40924c81669c118673ea365a206b4d2bfe9 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 17:56:44 -0700 Subject: [PATCH 09/10] quest: injected datagrams also leave the fuzz integrity oracle Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/fuzz-crypto.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md index c6b4be4894e..9b49c582e36 100644 --- a/quest/m1/quic/coverage/fuzz-crypto.md +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -18,9 +18,11 @@ Decided 2026-10-16 with the maintainer: - The `connection` target switches to it, so injected and mutated packets decrypt instead of being dropped at the AEAD. Today it runs about 64 inputs/s, almost all spent in the handshake. -- A decrypted injected packet can carry a valid STREAM frame, so the - target's delivered-bytes-match-written check excludes the streams injected - frames touch; no-panic still covers every input. +- Once packets decrypt, an injected or corrupted packet can carry valid + STREAM or DATAGRAM frames, so the target's integrity checks (stream bytes + match what was written, `check_datagram` matches what was sent) exclude + the streams and datagrams those packets touch; no-panic still covers every + input. - Not a debug transport: nothing outside the `fuzz` feature can name it. ## Required From da666c6326f151902bd49f40fcb2ba7852b5bb08 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 16 Oct 2026 19:45:19 -0700 Subject: [PATCH 10/10] quest: plaintext fuzz crypto is ready now the benchmarks landed Co-Authored-By: Claude Opus 5.5 --- quest/m1/quic/coverage/fuzz-crypto.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/quest/m1/quic/coverage/fuzz-crypto.md b/quest/m1/quic/coverage/fuzz-crypto.md index 9b49c582e36..a8bd437ed9f 100644 --- a/quest/m1/quic/coverage/fuzz-crypto.md +++ b/quest/m1/quic/coverage/fuzz-crypto.md @@ -13,7 +13,8 @@ Decided 2026-10-16 with the maintainer: - One implementation, in the hidden `fuzz` feature module. The Criterion benchmarks enable `--features fuzz`, as moq-net's already do, and replace - their bench-local no-op protection (ported from quinn `perf`'s + their bench-local no-op protection (`NoProtection*` in + `rs/moq-quic/benches/common/mod.rs`, ported from quinn `perf`'s `noprotection.rs`) with it. - The `connection` target switches to it, so injected and mutated packets decrypt instead of being dropped at the AEAD. Today it runs about 64 @@ -24,7 +25,3 @@ Decided 2026-10-16 with the maintainer: the streams and datagrams those packets touch; no-panic still covers every input. - Not a debug transport: nothing outside the `fuzz` feature can name it. - -## Required - -- [Benchmarks](/quest/m1/quic/coverage/bench.md) - lands the no-op protection this replaces