From 141f54aa30da94cb8dabc47299bfe01e77b1957f Mon Sep 17 00:00:00 2001 From: matthew-pilot Date: Fri, 29 May 2026 20:07:36 +0000 Subject: [PATCH] fix(beacon): narrow TOCTOU window in handlePunchRequest (PILOT-339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-snapshot the target and requester addresses immediately before each SendPunchCommand call to reduce the stale-address window. The underlying race is still possible (no locking across the UDP write) — UDP best-effort absorbs the remaining loss; the daemon retries on timeout. PILOT-339 --- server.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/server.go b/server.go index b8d9fa4..ad43cda 100644 --- a/server.go +++ b/server.go @@ -537,10 +537,25 @@ func (s *Server) handlePunchRequest(data []byte, remote *net.UDPAddr) { return } - // Send punch commands to both sides + // Narrow the TOCTOU window before each send: re-snapshot the target + // address immediately before telling the requester to punch at it. + // Between the initial snapshot above and now, the target may have + // re-registered from a different NAT binding. The window is still + // non-zero (no locking across the UDP write) — UDP best-effort + // absorbs the remaining loss; the daemon retries on timeout. + if addr, ok := s.nodes.Snapshot(targetID); ok { + targetAddr = addr + } if err := s.SendPunchCommand(requesterID, targetAddr.IP, uint16(targetAddr.Port)); err != nil { slog.Debug("punch command to requester failed", "node_id", requesterID, "err", err) } + + // Same re-snapshot for the requester address before telling the + // target to punch back. The requester's endpoint was upserted at + // the top of this function but may have flipped by now. + if addr, ok := s.nodes.Snapshot(requesterID); ok { + requesterAddr = addr + } if err := s.SendPunchCommand(targetID, requesterAddr.IP, uint16(requesterAddr.Port)); err != nil { slog.Debug("punch command to target failed", "node_id", targetID, "err", err) }