net/udprelay: fix peer relay server deadlock (#16542)

Fixes tailscale/corp#30381

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited 2025-07-11 17:12:23 -07:00 committed by GitHub
parent 39bf84d1c7
commit 24062e33d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -488,14 +488,17 @@ func (s *Server) listenOn(port int) error {
// Close closes the server.
func (s *Server) Close() error {
s.closeOnce.Do(func() {
s.mu.Lock()
defer s.mu.Unlock()
s.uc4.Close()
if s.uc6 != nil {
s.uc6.Close()
}
close(s.closeCh)
s.wg.Wait()
// s.mu must not be held while s.wg.Wait'ing, otherwise we can
// deadlock. The goroutines we are waiting on to return can also
// acquire s.mu.
s.mu.Lock()
defer s.mu.Unlock()
clear(s.byVNI)
clear(s.byDisco)
s.vniPool = nil
@ -564,6 +567,12 @@ func (s *Server) handlePacket(from netip.AddrPort, b []byte, rxSocket, otherAFSo
func (s *Server) packetReadLoop(readFromSocket, otherSocket *net.UDPConn) {
defer func() {
// We intentionally close the [Server] if we encounter a socket read
// error below, at least until socket "re-binding" is implemented as
// part of http://go/corp/30118.
//
// Decrementing this [sync.WaitGroup] _before_ calling [Server.Close] is
// intentional as [Server.Close] waits on it.
s.wg.Done()
s.Close()
}()