- DatagramReader::feed is infallible (the oversize bound is enforced at frame time; the same zombie category as 4014ba6's InvalidLength). The Vec<Result> shape, the local adapter's dead InvalidData mapping, and consumer.rs's `?` drop. - The sender-block machinery test (root-gated veth fixture, auto-skip without root): the peer address unassigned + the neighbor table flushed pins the connected sender in a persistent WouldBlock/ENOBUFS state; the test drives the WouldBlock stash, the readiness-Pending arms, AND the accepted-byte high-water Pend in one behavior, then resolves (addr assign + peer bind) and asserts the queued datagrams drain and deliver. Jumbo MTU (65535) keeps IP fragmentation out of the way; the block-state probe runs on a throwaway socket (an oversized probe's partially-built skb chain stays corked on EAGAIN and poisons the socket). - pump_queue_to_socket's stash arm now matches ENOBUFS(105) alongside WouldBlock — the blocked-neighbor state reports 105, and both mean "the socket cannot take this datagram now". - connect_udp's resolve-failure arm covered (an RFC 6761 .invalid target maps to dial_failed; rootless). - The no-witness tunnel_establisher wrapper covered (a harness DialNoWitness mode + a full open round-trip through the wrapper). Negative result pinned in the task doc: tokio 1.53 structurally masks UDP ICMP errors out of the poll drive (poll_recv_ready/try_io mask ERROR out of the read direction), so the recv-side ECONNREFUSED mapping arm is unreachable through the pump's drive — it joins the defensive list. Coverage: 92.86% -> 93.91% lines (50 -> 41 uncovered, all deliberate). Verification: cargo test + cargo test --all-features (91 tests), clippy --all-targets --all-features, fmt, wasm32 check+clippy, cargo doc — all clean.
7.0 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tunnels/coverage-weak-spots | Coverage weak spots — CLOSED (2026-09-09 second pass): the UdpHalf sender-block recipe found, feed infallible, wrappers covered | done |
|
single | low | component | implementation |
|
Resolution (2026-09-09, second pass)
The 2026-09-09 first pass (commits 4014ba6, 8f14fd1) closed the
behavioral gaps and left this task with the option-1-vs-3 decision.
The second pass found the sender-block recipe, took option 1, and
coverage moved 92.86% → 93.91% lines (50 → 41 uncovered). Three
commits:
DatagramReader::feedis infallible — the same zombie category as4014ba6'sInvalidLength: with the oversize bound enforced at frame time, the incremental decode cannot fail, so theVec<Result>shape andlocal/mod.rs's deadInvalidDatamapping were removed (pre-consumer window open per ADR-005's N-3).- The sender-block machinery test (
udp_half_write_side_blocks_ stashes_and_drains_on_resolve) — the recipe that reached the WouldBlock stash, the readiness-Pending arms, AND the high-water backpressure in one coherent behavior (option 1). - The thin wrappers —
connect_udp's resolve-failure arm (.invalidNXDOMAIN →dial_failed) and the no-witnesstunnel_establisher(a harnessDialNoWitnessmode + a full open round-trip through the wrapper).
The sender-block recipe (the host-blocking finding, RESOLVED)
UdpHalf's write-side machinery needed a UDP socket whose try_send
persistently reports WouldBlock/ENOBUFS. The recipe (validated
3/3 deterministic on this host, kernel 6.8.0-110-generic, tokio
1.53.1, socket2 0.6.5):
- A veth pair with the peer address UNASSIGNED + a flushed
neighbor table. The connected sender's datagrams queue on the
unresolved neighbor while charged to
sk_wmem_alloc; with a shrunkenSO_SNDBUFthe charge exceeds the buffer → persistentEAGAIN/ENOBUFS(the drain reports 105=ENOBUFS alongside EAGAIN — both mean "the socket cannot take this datagram now", which thepump_queue_to_socketstash arm now matches too). - Jumbo MTU (65535) on the fixture: a 60 KB datagram crosses as ONE frame — no IP fragmentation, so no fragment-reassembly races at the peer (fragment-ID collisions under the drain burst merged datagrams on an MTU-1500 veth; the peer reassembled 5-6 datagrams into single ~43947-byte payloads).
- The block-state probe runs on a THROWAWAY socket: an oversized
probe datagram's partially-built skb chain stays corked in
sk_write_queueon EAGAIN and flushes on the next successful send — poisoning the socket with a stale partial datagram. The test's real socket is created fresh after the probe. - Assigning the peer address + binding the peer resolves ARP; the queued/stashed datagrams drain and DELIVER (the resolve step).
- Root-gated (
ip link add): the fixture tries bareipthensudo ipand auto-skips loud-free without either.
The first pass's kernel findings still hold and are why the loopback
recipes failed there: a non-reading peer's full receive queue DROPS
datagrams silently (loopback/veth/netem — the sender never blocks);
netem with limit still did not block (2M sends Ok in a probe).
Negative result: the ICMP ECONNREFUSED arm is unreachable through the poll drive
A connected UDP socket + a closed peer port elicits an ICMP port
unreachable that ECONNREFUSEDs the next recvfrom (a plain std
nonblocking recv surfaces it deterministically — probed rootless).
But tokio 1.53 structurally masks it out of the poll drive (the
adapter's shape): the EPOLLOUT|EPOLLERR event maps to
WRITABLE|ERROR|WRITE_CLOSED; poll_recv_ready's Direction::Read
mask is READABLE|READ_CLOSED (ERROR excluded) → Pending forever;
try_io(READABLE) intersects empty → WouldBlock. Only the async
recv() (async_io with READABLE|ERROR) surfaces ECONNREFUSED
— and UdpHalf::poll_read is a poll impl. Verified by probes
(poll_recv, poll_recv_ready, try_recv all Elapsed; the async
recv surfaced it exactly once, racing the waiter registration —
nondeterministic). local/mod.rs:356 therefore joins the defensive
list: no deterministic test can reach it through the pump's drive on
tokio 1.53.
The final uncovered-line inventory (41 lines, all deliberate)
src/local/mod.rs:136-137— the no-addresses resolve arm: a name that RESOLVES to zero addresses is unreachable via getaddrinfo (NXDOMAIN errors first — the.invalidtest hits the error arm).src/local/mod.rs:321-333— theOversizemapping at the recv scratch (a >65535-byte datagram cannot exist on a real wire — the IP theoretical max is 65507).src/local/mod.rs:351-352,356— the recv-side readiness-Pending arms (genuine WouldBlock) + the ICMP error mapping (the negative result above).src/local/mod.rs:378-379,383— the high-water drain's Ready(Ok)/Ready(Err) sub-arms (the observed over-line poll rides the Pending sub-arm; the Ok sub-arm needs the drain to succeed within that single poll — the FAILED-cycle release timing).src/local/mod.rs:476-477—pump_queue_to_socket'spoll_send_readyReady(Ok)/Err sub-arms (the Pend sub-arm is the covered one).src/consumer.rs:91-94,405-406—serde_json::to_valuefailures onTunnelParams(impossible by construction).src/consumer.rs:205,290—unreachable!guards on spent sessions.src/consumer.rs:285— the stale-pump abort (defensive only).src/consumer.rs:325-330— the pumpJoinErrortelemetry arm (needs the spawned pump to panic/abort — not producible through the session API).src/producer.rs:85-86— theHandlerErrormapping (the schema gate preempts it; pinned byschema_gate_rejects_missing_substrate_…).src/producer.rs:338-347— the pump-handler telemetry early-returns (accept_bifailure needs a dead channel; the plan is alwaysTargetHandle;Arc::try_unwrapneeds a second clone).src/wire.rs:77-79— theDefaultimpl shim (clippy'snew_without_default).src/wire.rs:106— theunreachable!invariant infeed's loop.src/wire.rs:177— a unit test's own panic line (the negative branch ofdatagram_split_across_awkward_chunks_reassembles).
Verification (2026-09-09, second pass)
cargo llvm-cov --all-features --workspace --summary-only— 93.91% lines (from 92.86%; 50 → 41 uncovered, all deliberate).cargo test --all-features— 91 tests green (15 lib + 16 consumer_session + 18 end_to_end + 13 doc/lib + 19 local_halves- 9 producer_listen + 13 producer_open_op + 1 doc-test compile_fail).
cargo test(default) — green; the root-gated veth test skips loud-free without root.cargo clippy --all-targets --all-features -- -D warningsandcargo fmt --check— clean.cargo check --target wasm32-unknown-unknown+cargo clippy --target wasm32-unknown-unknown -- -D warnings— clean (the default crate stays wasm-clean).