From b5773d808a1c2db6501f2d5b9da706478eccbeea Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Fri, 11 Aug 2023 09:53:01 +0000 Subject: [PATCH] Don't process activity during closing Signed-off-by: Spike Curtis --- wgengine/userspace.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/wgengine/userspace.go b/wgengine/userspace.go index e29e20daec6b8..6c9ec3768c6f2 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -532,6 +532,16 @@ func (e *userspaceEngine) noteRecvActivity(nk key.NodePublic) { e.wgLock.Lock() defer e.wgLock.Unlock() + // Reconfiguring wireguard while closing can cause deadlocks, since this + // function could be called from receive functions that need to shut down + // in order to close the wireguard device. + e.mu.Lock() + if e.closing { + e.mu.Unlock() + return + } + e.mu.Unlock() + if _, ok := e.recvActivityAt[nk]; !ok { // Not a trimmable peer we care about tracking. (See isTrimmablePeer) if e.trimmedNodes[nk] { @@ -1058,16 +1068,24 @@ func (e *userspaceEngine) RequestStatus() { } func (e *userspaceEngine) Close() { + // It's important here to hold wgLock as well, so that noteRecvActivity exits early once we start + // closing the wgdev. Reprogramming wireguard from a recv function causes a deadlock while closing + // since the wireguard device waits for the recv function. + e.wgLock.Lock() e.mu.Lock() if e.closing { e.mu.Unlock() + e.wgLock.Unlock() return } e.closing = true e.mu.Unlock() + e.wgLock.Unlock() r := bufio.NewReader(strings.NewReader("")) + e.wgdev.IpcSetOperation(r) + e.magicConn.Close() e.netMonUnregister() if e.netMonOwned { @@ -1075,7 +1093,9 @@ func (e *userspaceEngine) Close() { } e.dns.Down() e.router.Close() + e.wgdev.Close() + e.tundev.Close() if e.birdClient != nil { e.birdClient.DisableProtocol("tailscale")