ipn/ipnlocal: don't panic if there are no suitable exit nodes
In suggestExitNodeLocked, if no exit node candidates have a home DERP or
valid location info, `bestCandidates` is an empty slice. This slice is
passed to `selectNode` (`randomNode` in prod):
```go func randomNode(nodes views.Slice[tailcfg.NodeView], …) tailcfg.NodeView {
…
return nodes.At(rand.IntN(nodes.Len()))
}
```
An empty slice becomes a call to `rand.IntN(0)`, which panics.
This patch changes the behaviour, so if we've filtered out all the
candidates before calling `selectNode`, reset the list and then pick
from any of the available candidates.
This patch also updates our tests to give us more coverage of `randomNode`,
so we can spot other potential issues.
Updates #17661
Change-Id: I63eb5e4494d45a1df5b1f4b1b5c6d5576322aa72
Signed-off-by: Alex Chan <alexc@tailscale.com>
This commit is contained in:
@@ -7432,6 +7432,16 @@ func suggestExitNodeUsingDERP(report *netcheck.Report, nb *nodeBackend, prevSugg
|
||||
}
|
||||
}
|
||||
bestCandidates := pickWeighted(pickFrom)
|
||||
|
||||
// We may have an empty list of candidates here, if none of the candidates
|
||||
// have home DERP info.
|
||||
//
|
||||
// We know that candidates is non-empty or we'd already have returned, so if
|
||||
// we've filtered everything out of bestCandidates, just use candidates.
|
||||
if len(bestCandidates) == 0 {
|
||||
bestCandidates = candidates
|
||||
}
|
||||
|
||||
chosen := selectNode(views.SliceOf(bestCandidates), prevSuggestion)
|
||||
if !chosen.Valid() {
|
||||
return res, errors.New("chosen candidate invalid: this is a bug")
|
||||
|
||||
Reference in New Issue
Block a user