From 8def19181a4b896965e913d44d1de706b6a4eb85 Mon Sep 17 00:00:00 2001 From: Tanvir Date: Wed, 22 Apr 2026 22:30:49 +0800 Subject: [PATCH] p2p/discover: apply same fix to gotreply and resetTimeout loops Both loops also called plist.Remove(el) while iterating with el = el.Next(), which invalidates the link and skips the rest of the list. Save el.Next() before removal like the timeout loop. --- p2p/discover/v4_udp.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 8702aa5d3f..3a8227abc7 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -446,7 +446,8 @@ func (t *UDPv4) loop() { } // Start the timer so it fires when the next pending reply has expired. now := time.Now() - for el := plist.Front(); el != nil; el = el.Next() { + for el := plist.Front(); el != nil; { + next := el.Next() nextTimeout = el.Value.(*replyMatcher) if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { timeout.Reset(dist) @@ -457,6 +458,7 @@ func (t *UDPv4) loop() { // backwards after the deadline was assigned. nextTimeout.errc <- errClockWarp plist.Remove(el) + el = next } nextTimeout = nil timeout.Stop() @@ -478,7 +480,8 @@ func (t *UDPv4) loop() { case r := <-t.gotreply: var matched bool // whether any replyMatcher considered the reply acceptable. - for el := plist.Front(); el != nil; el = el.Next() { + for el := plist.Front(); el != nil; { + next := el.Next() p := el.Value.(*replyMatcher) if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip { ok, requestDone := p.callback(r.data) @@ -492,6 +495,7 @@ func (t *UDPv4) loop() { // Reset the continuous timeout counter (time drift detection) contTimeouts = 0 } + el = next } r.matched <- matched