Findings + review doc: docs/reviews/001-implementation-review.md (the alkcall house pattern). Closes tunnels/review-core-crates and tunnels/review-impl. Findings: - U-1 [major, NOT fixed] — the local UDP adapter is not the framed adapter ADR-003 mandates: codec-framed bytes reach real targets, empty datagrams are EOF-shaped at the adapter (tunnel teardown), >8 KiB datagrams split, unframed target-initiated datagrams are dropped. Executable-pinned by real-socket probes (temporary test, deleted after the run). Remediation task: tunnels/fix-udp-framed-adapter. - C-1 [major, fixed] — AcceptQueue::pop lost wakeup in the check→register window (open op hangs to the establishment bound); fixed via the tokio notify_waiters contract (Notified created before the check each iteration) + multi-thread regression test. - N-4/N-5/N-7/N-8/N-9/N-10/N-11 [minor, fixed] — serialize TunnelParams in open_reverse_channel; stale doc ref; broken doc link; root re-exports (TunnelParams/Substrate/tunnel_open_spec/OP_TUNNEL_OPEN); README (publish dry-run was failing on the missing readme); harness comment; pump_against UDP framing doc note. - N-2 retracted by executable falsification (codec buffering is bounded by construction); N-6 non-finding with rationale. Verified: 63 tests default / 70 with --features local, 3x repeat-run clean both configs; clippy -D warnings (all-targets + wasm32); fmt; doc warning-free; wasm32 check; cargo publish --dry-run passes.
82 lines
3.4 KiB
Markdown
82 lines
3.4 KiB
Markdown
---
|
||
id: tunnels/fix-udp-framed-adapter
|
||
name: "U-1 remediation — make UdpHalf the FRAMED adapter (ADR-003 at the local boundary)"
|
||
status: pending
|
||
depends_on: [tunnels/review-impl]
|
||
scope: moderate
|
||
risk: medium
|
||
impact: component
|
||
level: implementation
|
||
tags: [local, udp, codec, adr-003, review-remediation]
|
||
---
|
||
|
||
## Description
|
||
|
||
Review 001 finding U-1 (`docs/reviews/001-implementation-review.md`):
|
||
the `local` feature's `UdpHalf` (`src/local/mod.rs`) is NOT the framed
|
||
adapter ADR-003 mandates at the substrate boundary — it is
|
||
boundary-preserving (one datagram per poll_read/poll_write) but raw.
|
||
The `[len: u16 BE]` codec exists only on the session side, so
|
||
UDP-over-`local` tunnels:
|
||
|
||
1. deliver codec-framed bytes to real targets (the target sees
|
||
`[len]payload`, not payload — executable-pinned in the review
|
||
probes);
|
||
2. tear the tunnel down on an empty datagram (a raw zero-length
|
||
datagram is `Ok(())` with 0 bytes filled = EOF-shaped to
|
||
`tokio::io::copy` — the F-2 violation end-to-end; the session-side
|
||
codec exists precisely to prevent this);
|
||
3. split datagrams > ~8 KiB at the target (`tokio::io::copy`'s
|
||
`DEFAULT_BUF_SIZE` bounds each read; the raw adapter sends each
|
||
chunk as its own datagram);
|
||
4. silently drop unframed target-initiated datagrams (the session
|
||
codec parses a raw datagram's first two bytes as a length header).
|
||
|
||
The session-side tests pass because the harness's
|
||
`framed_udp_echo_dial` frames symmetrically at the test boundary — a
|
||
correct-shape stand-in for what `connect_udp` itself must do.
|
||
|
||
### The fix
|
||
|
||
Apply ADR-003's placement: `UdpHalf::poll_write` frames the caller's
|
||
buffer (`frame_datagram`) and sends one datagram per framed frame;
|
||
`poll_read` de-frames (feed `DatagramReader`, emit the next payload).
|
||
Both halves of the association then speak the same framing; `len=0`
|
||
becomes a 2-byte frame (unambiguous with EOF — the F-2 fix
|
||
end-to-end).
|
||
|
||
### Verification gates (from the review)
|
||
|
||
1. A recording UDP echo server asserts the datagrams it receives are
|
||
**payload bytes only** and an **empty datagram round-trips**
|
||
(session `recv_datagram` yields `Some(b"")`, channel alive).
|
||
2. A >8 KiB datagram round-trips as ONE datagram at the target.
|
||
3. An unframed target-initiated datagram resolves at the session.
|
||
4. The existing `local_halves` suite stays green unchanged (its
|
||
session-level round-trips are framing-symmetric today).
|
||
5. `cargo test` + `--features local` + wasm checks as usual; 3×
|
||
repeat-run stable.
|
||
|
||
### Riders (from the same review, same file)
|
||
|
||
- U-2: `connect_udp` binds `("0.0.0.0", 0)` — resolve the target
|
||
family first (or document the v4-only posture).
|
||
- U-3: size the recv scratch to the codec's MTU discipline instead of
|
||
a per-tunnel 65535-byte allocation (or justify the bound).
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] `UdpHalf` frames on write and de-frames on read (ADR-003's
|
||
placement; the pump stays raw)
|
||
- [ ] The 5 verification gates pass; suites green 3× in both configs
|
||
- [ ] U-2/U-3 riders resolved or explicitly deferred with rationale
|
||
- [ ] Review 001's U-1/U-2/U-3/N-1 sections get resolution notes
|
||
|
||
## References
|
||
|
||
- `docs/reviews/001-implementation-review.md` §U-1 (executable-pinned
|
||
probe evidence), §U-2, §U-3, §N-1
|
||
- `docs/architecture/decisions/003-codec-and-udp-framing.md` (the
|
||
framed-adapter mandate + F-2), `docs/architecture/wire.md`
|
||
§Datagram substrate
|
||
- `src/local/mod.rs` (`UdpHalf`), `src/wire.rs` (the codec to compose) |