Skip to content

Commit de4696d

Browse files
committed
wgengine/magicsock: fix deadlock on shutdown
This fixes a deadlock on shutdown. One goroutine is waiting to send on c.derpRecvCh before unlocking c.mu. The other goroutine is waiting to lock c.mu before receiving from c.derpRecvCh. tailscale#3736 has a more detailed explanation of the sequence of events. Fixes tailscale#3736 Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
1 parent 390490e commit de4696d

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

wgengine/magicsock/magicsock.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ type Conn struct {
265265
stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr)
266266

267267
// derpRecvCh is used by receiveDERP to read DERP messages.
268+
// It must have buffer size > 0; see issue 3736.
268269
derpRecvCh chan derpReadResult
269270

270271
// bind is the wireguard-go conn.Bind for Conn.
@@ -529,7 +530,7 @@ func (o *Options) derpActiveFunc() func() {
529530
// of NewConn. Mostly for tests.
530531
func newConn() *Conn {
531532
c := &Conn{
532-
derpRecvCh: make(chan derpReadResult),
533+
derpRecvCh: make(chan derpReadResult, 1), // must be buffered, see issue 3736
533534
derpStarted: make(chan struct{}),
534535
peerLastDerp: make(map[key.NodePublic]int),
535536
peerMap: newPeerMap(),
@@ -2647,6 +2648,7 @@ func (c *connBind) Close() error {
26472648
}
26482649
// Send an empty read result to unblock receiveDERP,
26492650
// which will then check connBind.Closed.
2651+
// connBind.Closed takes c.mu, but c.derpRecvCh is buffered.
26502652
c.derpRecvCh <- derpReadResult{}
26512653
return nil
26522654
}

0 commit comments

Comments
 (0)