From c18f8516c4d7f6339ea586c3f90d1025c9335bcd Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Fri, 10 Apr 2026 13:00:34 -0400 Subject: [PATCH] ipn/ipnlocal: fix deadlock in ipn bus watcher fixes tailscale/corp#40401 From an internal stack trace. We witnessed a slow bus error and a complete lock up of the ipn bus watcher. Stack showed a b.mu->t.mu and t.mu->b.mu lock ordering issue when setting up the HealthTracker's initial state and its timer simultaneously fires. Signed-off-by: Jonathan Nobels --- ipn/ipnlocal/local.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 610d1d7b5..9aa7dc899 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -3233,9 +3233,6 @@ func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.A if mask&ipn.NotifyInitialDriveShares != 0 && b.DriveSharingEnabled() { ini.DriveShares = b.pm.prefs.DriveShares() } - if mask&ipn.NotifyInitialHealthState != 0 { - ini.Health = b.HealthTracker().CurrentState() - } if mask&ipn.NotifyInitialSuggestedExitNode != 0 { if en, err := b.suggestExitNodeLocked(); err == nil { ini.SuggestedExitNode = &en.ID @@ -3261,6 +3258,10 @@ func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.A mak.Set(&b.notifyWatchers, sessionID, session) b.mu.Unlock() + if mask&ipn.NotifyInitialHealthState != 0 && ini != nil { + ini.Health = b.HealthTracker().CurrentState() + } + metricCurrentWatchIPNBus.Add(1) defer metricCurrentWatchIPNBus.Add(-1)