From eddd019ee4dd66bd8d60c7dd7605456beb93de25 Mon Sep 17 00:00:00 2001 From: Simon Law Date: Mon, 15 Jun 2026 13:14:12 -0700 Subject: [PATCH] ipn/ipnlocal: protect populatePeerStatusLocked from nil Hostinfo (#20150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ipnlocal.LocalBackend.populatePeerStatusLocked assumed that Hostinfo was always valid, but that’s not always true, especially in tests. ipnlocal.peerAPIPorts suffered from a similar assumption. This patch checks for NodeView.Valid and Hostinfo.Valid; assuming the zero value as a safe default. Updates #8948 Updates #12542 Signed-off-by: Simon Law --- ipn/ipnlocal/local.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 7811612b8..c5bdd3f0f 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1611,6 +1611,7 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) { sb.AddUser(id, up) } exitNodeID := b.pm.CurrentPrefs().ExitNodeID() + blankHostinfo := new(tailcfg.Hostinfo).View() for _, p := range cn.Peers() { tailscaleIPs := make([]netip.Addr, 0, p.Addresses().Len()) for i := range p.Addresses().Len() { @@ -1619,20 +1620,24 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) { tailscaleIPs = append(tailscaleIPs, addr.Addr()) } } + hostinfo := p.Hostinfo() + if !hostinfo.Valid() { + hostinfo = blankHostinfo + } ps := &ipnstate.PeerStatus{ InNetworkMap: true, UserID: p.User(), AltSharerUserID: p.Sharer(), TailscaleIPs: tailscaleIPs, - HostName: p.Hostinfo().Hostname(), + HostName: hostinfo.Hostname(), DNSName: p.Name(), - OS: p.Hostinfo().OS(), + OS: hostinfo.OS(), LastSeen: p.LastSeen().Get(), Online: p.Online().Get(), - ShareeNode: p.Hostinfo().ShareeNode(), + ShareeNode: hostinfo.ShareeNode(), ExitNode: p.StableID() != "" && p.StableID() == exitNodeID, - SSH_HostKeys: p.Hostinfo().SSH_HostKeys().AsSlice(), - Location: p.Hostinfo().Location().AsStruct(), + SSH_HostKeys: hostinfo.SSH_HostKeys().AsSlice(), + Location: hostinfo.Location().AsStruct(), Capabilities: p.Capabilities().AsSlice(), } for _, f := range b.extHost.Hooks().SetPeerStatus { @@ -7654,6 +7659,9 @@ func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error { } func peerAPIPorts(peer tailcfg.NodeView) (p4, p6 uint16) { + if !peer.Valid() || !peer.Hostinfo().Valid() { + return + } svcs := peer.Hostinfo().Services() for _, s := range svcs.All() { switch s.Proto {