fix(netcheck): reject unavailable interface state before DERP probes
Summary
- Fail closed with a descriptive error when interface enumeration leaves
netmon.NewStatic()without an interface snapshot. - Keep normal DERP region selection unchanged; do not change dependencies, networking permissions, or MASQUE transport.
- Add a deterministic zero-monitor regression test. Without the guard it reproduces the nil dereference in Tailscale netcheck's probe planner; with the guard it returns an error before probes start.
Root cause
Tailscale netmon.NewStatic() intentionally suppresses an interface enumeration error and leaves InterfaceState() nil. In restricted environments, PickBestRegion previously passed that monitor to netcheck.GetReport, whose makeProbePlanInitial dereferences the missing snapshot. Existing caller error paths already report a returned error.
Validation
- Red: regression test reproduced the original panic in
net/netcheck/netcheck.go:572(tailscale.com v1.103.0-pre.0.20260830144538-72780705eda8). - Green: regression test passes, including three race-detector repetitions.
- MASQUE/StreamForwarder/ReceiveStreamDatagram/PickBestRegion focused tests with race detector passed; CLI MASQUE-focused tests passed; relay command full race tests passed.
go build ./...,go vet ./..., formatting/diff checks passed.- golangci-lint v2.15.0 against base 0178c542: 0 new issues.
- Full baseline
go test ./...was executed and is NOT green in this cloud sandbox: 4 legacy root tests fail on denied netlink enumeration and 11 legacy CLI integration tests fail while awaiting server startup. The patch handles the panic but does not grant network permissions or bypass these tests.
Separate local acceptance evidence
A scratch-only loopback harness (not included in this PR) passed HTTP/3 relay → WireGuard → gVisor → TCP echo with 1,114,112 bytes and matching SHA-256, rejected a mismatched TLS hostname, and returned a read timeout after relay shutdown under a 1.5 s application deadline. Three race repetitions passed. This does not prove public-network/NAT, runtime direct-to-relay migration, or physical-device acceptance.
No merge, deployment, release, or permission change is requested by this draft.
Summary by cubic
Fails closed with a descriptive error when netmon.NewStatic() cannot enumerate network interfaces, fixing a nil-pointer panic during DERP region selection. Previously PickBestRegion passed the monitor straight into netcheck, whose probe planner dereferenced the missing interface snapshot in restricted environments.
- Refactors
PickBestRegioninto a testablepickBestRegionthat accepts an injectable monitor. - Adds a regression test; without the guard it reproduces the original panic in
makeProbePlanInitial.
Summary by Sourcery
Fail closed during DERP region selection when the network interface snapshot is unavailable.
Bug Fixes:
- Prevent DERP region selection from panicking when network interface enumeration provides no interface state by returning a descriptive error before netcheck probes begin.
Enhancements:
- Refactor DERP region selection to accept an injectable network monitor for deterministic validation.
Tests:
- Add a regression test covering unavailable interface state and confirming that no region is selected.
Summary by CodeRabbit
- Bug Fixes
- Region selection now returns an error when network interface state is unavailable, rather than proceeding without a valid snapshot. This makes failures in that situation clearer and more predictable.