AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
High 73 Bitcoin

server: fix peer add/done race between peerHandler and syncManager

Public commit record

What the developer wrote

Authored by Or Aharonee

85/100 · Strong
server: fix peer add/done race between peerHandler and syncManager

peerDoneHandler ran as a separate goroutine per peer and independently
notified both peerHandler (via donePeers channel) and the sync manager
(via syncManager.DonePeer) about a peer disconnect. Because these two
sends were unsynchronized, the sync manager could observe DonePeer
before NewPeer when a peer connected and disconnected quickly. This
caused the sync manager to log "unknown peer", then later register the
already-dead peer as a sync candidate that was never cleaned up,
potentially leaving it stuck with a dead sync peer.

Two structural changes eliminate the race:

1. Merge the newPeers and donePeers channels into a single
peerLifecycle channel. Since OnVerAck (add) always fires before
WaitForDisconnect returns (done), a single FIFO channel guarantees
peerHandler always processes add before done for a given peer,
removing the select-ambiguity where Go could pick done first.

2. Move the syncManager.DonePeer call and orphan eviction from
peerDoneHandler into handleDonePeerMsg, which runs inside
peerHandler. All sync manager peer lifecycle notifications now
originate from the single peerHandler goroutine and flow into
sm.msgChan in guaranteed add-before-done order.
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode
The short version

What changed, and why it matters

This commit fixes a race condition in btcd, a Bitcoin node implementation. When a peer connected and disconnected very quickly, the node could tell its block-syncing component that the peer left before it ever told it the peer arrived. That left the syncer believing a dead peer was still its active sync partner, so the node could stop syncing new blocks from real peers. The fix funnels all peer add/remove events through one ordered channel so the syncer always sees arrival before departure. A new integration test demonstrates the bug by bombarding a node with handshake-then-disconnect connections and then checking whether it still syncs blocks.

Recommended action

Apply the patch. The fix is structural and well-scoped, but operators should monitor sync behavior after upgrade because the bug could leave a node stuck on an old chain tip. Consider running the new integration test under the rpctest build tag to validate the fix in your environment.

Security signals we found

01

Race condition between peer add and done notifications to the sync manager

02

Sync manager could observe DonePeer before NewPeer for a rapidly disconnecting peer

03

Dead peer could remain registered as the active sync peer, stalling block synchronization

04

New integration test demonstrates reproducible sync stall under handshake-and-drop load

05

Fix centralizes peer lifecycle notifications through a single FIFO channel and single goroutine

Risk score

Why this scored 73/100

Our methodology →
Potential impact 22/30
Exploitability 16/25
Stealth signal 11/15
Affected reach 12/15
Confidence 8/10
Evidence quality 4/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.