mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-10 01:56:37 +00:00
p2p/discover: restore nextTimeout update in UDPv4 resetTimeout loop (#34878)
The refactor from `for el := plist.Front(); ...; el = el.Next()` to the new `iterList` iterator in #34743 silently dropped two things needed by resetTimeout: 1. `nextTimeout = el.Value.(*replyMatcher)` at the top of the loop. This assignment is what gives `nextTimeout` its documented meaning ("head of plist when timeout was last reset"), and what makes the early-return optimization at the top of resetTimeout work. Without it, nextTimeout is only ever written to nil, so `nextTimeout == plist.Front().Value` is always false and the optimization is dead. 2. `nextTimeout.errc <- errClockWarp` in the clock-warp branch now reads a stale or nil pointer. Prior to the refactor, the inner assignment kept nextTimeout pointing at the current matcher so its errc was the right channel to receive the errClockWarp signal. After the refactor, on first entry into the clock-warp branch nextTimeout is nil, which panics the UDPv4 loop goroutine with a nil pointer deref and takes discv4 down. Re-assign `nextTimeout = p` at the head of the loop (restoring the documented invariant) and send the clock-warp error on `p.errc` rather than the now-stale `nextTimeout.errc`. The clock-warp branch triggers only when the system clock jumps backward after a deadline is assigned (deadline - time.Now() >= 2*respTimeout, i.e. at least ~500ms backward jump), which is why this regression slipped past CI - it is not exercised by any existing unit test, and writing one would require plumbing a clock through the loop.
This commit is contained in:
parent
5b837e5786
commit
60db25b070
1 changed files with 2 additions and 1 deletions
|
|
@ -447,6 +447,7 @@ func (t *UDPv4) loop() {
|
||||||
// Start the timer so it fires when the next pending reply has expired.
|
// Start the timer so it fires when the next pending reply has expired.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for p, el := range iterList[*replyMatcher](plist) {
|
for p, el := range iterList[*replyMatcher](plist) {
|
||||||
|
nextTimeout = p
|
||||||
if dist := p.deadline.Sub(now); dist < 2*respTimeout {
|
if dist := p.deadline.Sub(now); dist < 2*respTimeout {
|
||||||
timeout.Reset(dist)
|
timeout.Reset(dist)
|
||||||
return
|
return
|
||||||
|
|
@ -454,7 +455,7 @@ func (t *UDPv4) loop() {
|
||||||
// Remove pending replies whose deadline is too far in the
|
// Remove pending replies whose deadline is too far in the
|
||||||
// future. These can occur if the system clock jumped
|
// future. These can occur if the system clock jumped
|
||||||
// backwards after the deadline was assigned.
|
// backwards after the deadline was assigned.
|
||||||
nextTimeout.errc <- errClockWarp
|
p.errc <- errClockWarp
|
||||||
plist.Remove(el)
|
plist.Remove(el)
|
||||||
}
|
}
|
||||||
nextTimeout = nil
|
nextTimeout = nil
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue