acpmux: WireGuard loopback test checks UDP release in this process (fork-copy flake)
Flake fix for a red seen on base: wireguard::tests::loopback_throughput_and_shutdown failed at wireguard_tests.rs:313 with "shutdown must release UDP" in about 1 of 5 full cargo test -p acpmux --lib runs (11 of 60 at base 13531e30, and 9 of 40 in the #27423 runs). Fixing a base red; never blocked.
The cause
After a.shutdown(), the test rebinds the transport's UDP port and expects that to succeed. That rebind also fails if another process holds a copy of the socket. Two kinds of process can hold one:
- A child that another test is spawning. It has a copy of every open descriptor from its fork until its exec.
- A child that forks and never execs.
linux_sandbox::seccomp::tests::installed_filter_enforces_syscalls_in_forked_childforks a child that runs checks and_exits. That child holds the copy for its whole life.
The kernel keeps the port bound until the last copy closes, so the rebind fails with EADDRINUSE even though no descriptor of ours holds the socket.
Diagnostics from failing runs at base (an instrumented copy, not committed; run):
- This process no longer held the socket.
/proc/net/udpstill listed it, by its own inode.- The one other holder was a child of the test binary named after the
linux_sandbox::test thread. It was forked and not exec'd. - In the one failure where that child had already exited, a second bind made at once succeeded.
Reproduced deterministically: a child that holds a copy of the socket (dup'd in pre_exec, then sleep) makes this test fail 5 of 5 at base (run).
The fix
Test only. What shutdown must do is close this process's socket. On Linux the test now checks that through /proc/self/fd:
- Before shutdown, a descriptor of this process is the transport's socket (
socket:[inode]), so the check is not vacuous. - After shutdown, none is.
The test still checks the rest of shutdown as before (a pending stream read ends, b shuts down, the echo server finishes). Other systems keep the rebind check. Production code is unchanged. The daemon rebinds after a restart in a new process, and there a short-lived spawn's copy closes at its exec.
Evidence (Linux, cmux-ci test, 32 vCPU)
| run | tree | result |
|---|---|---|
3 x 20 full cargo test -p acpmux --lib, diagnostics | base 13531e30 | this test failed 3, 4 and 4 of 20 (1, 2, 3) |
| 5x this test, a child holds a copy of the socket | base 13531e30 | failed 5/5 (run) |
| 5x the same held copy, then 20x with 8 threads spawning beside it | this head | passed 5/5 and 20/20 (run) |
30x full cargo test -p acpmux --lib | this head | this test passed 30/30 (run) |
cargo clippy -p acpmux --all-targets -- -D warnings, cargo fmt --check | this head | clean (run) |
The other reds in those full runs are not this test, and they fail the same way at base:
protocol::tests::protocol_ir_is_current: deterministic. The spec has been stale since #27216.cli::machine_consent::tests::tags_share_enrollment_and_live_grants_and_revocation: a flock flake.
Each has its own PR.
Risk: low, test-only, one file.
Agent session: agent session
š¤ Generated with Claude Code
Summary by cubic
Fixes a flake in wireguard::tests::loopback_throughput_and_shutdown where the test verified UDP release by rebinding the transport's UDP port after shutdown. That rebind could fail with EADDRINUSE when a process spawned by another test held a fork-copy of the socket, even though this process had already closed it.
On Linux, the test now checks via /proc/self/fd that no descriptor of this process is the transport's socket, both before and after shutdown. Non-Linux systems keep the rebind check. Production code is unchanged.