Files
alktunnels/tasks/tunnels/coverage-weak-spots.md
T
glm-5.3-flash 63dbdb64a5 test: coverage weak spots — the UdpHalf sender-block recipe found (option 1)
- 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.
2026-09-09 08:52:23 +00:00

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
tunnels/review-impl
single low component implementation
tests
coverage
udp
review-remediation

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:

  1. DatagramReader::feed is infallible — the same zombie category as 4014ba6's InvalidLength: with the oversize bound enforced at frame time, the incremental decode cannot fail, so the Vec<Result> shape and local/mod.rs's dead InvalidData mapping were removed (pre-consumer window open per ADR-005's N-3).
  2. 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).
  3. The thin wrappersconnect_udp's resolve-failure arm (.invalid NXDOMAIN → dial_failed) and the no-witness tunnel_establisher (a harness DialNoWitness mode + 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 shrunken SO_SNDBUF the charge exceeds the buffer → persistent EAGAIN/ENOBUFS (the drain reports 105=ENOBUFS alongside EAGAIN — both mean "the socket cannot take this datagram now", which the pump_queue_to_socket stash 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_queue on 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 bare ip then sudo ip and 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 .invalid test hits the error arm).
  • src/local/mod.rs:321-333 — the Oversize mapping 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-477pump_queue_to_socket's poll_send_ready Ready(Ok)/Err sub-arms (the Pend sub-arm is the covered one).
  • src/consumer.rs:91-94,405-406serde_json::to_value failures on TunnelParams (impossible by construction).
  • src/consumer.rs:205,290unreachable! guards on spent sessions.
  • src/consumer.rs:285 — the stale-pump abort (defensive only).
  • src/consumer.rs:325-330 — the pump JoinError telemetry arm (needs the spawned pump to panic/abort — not producible through the session API).
  • src/producer.rs:85-86 — the HandlerError mapping (the schema gate preempts it; pinned by schema_gate_rejects_missing_substrate_…).
  • src/producer.rs:338-347 — the pump-handler telemetry early-returns (accept_bi failure needs a dead channel; the plan is always TargetHandle; Arc::try_unwrap needs a second clone).
  • src/wire.rs:77-79 — the Default impl shim (clippy's new_without_default).
  • src/wire.rs:106 — the unreachable! invariant in feed's loop.
  • src/wire.rs:177 — a unit test's own panic line (the negative branch of datagram_split_across_awkward_chunks_reassembles).

Verification (2026-09-09, second pass)

  • cargo llvm-cov --all-features --workspace --summary-only93.91% lines (from 92.86%; 50 → 41 uncovered, all deliberate).
  • cargo test --all-features91 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 warnings and cargo fmt --check — clean.
  • cargo check --target wasm32-unknown-unknown + cargo clippy --target wasm32-unknown-unknown -- -D warnings — clean (the default crate stays wasm-clean).