netsync: process inv announcements when no syncPeer is set
What changed, and why it matters
This commit fixes a logic bug in btcd's block synchronization code. When two Bitcoin nodes connected at the same block height, the node would refuse to process new-block announcements from its peer, causing synchronization to stall (a deadlock). The fix allows those announcements to be processed when no dedicated sync peer has been selected, so the node can continue downloading new blocks.
Treat as a reliability/availability fix rather than an exploitable security vulnerability. Operators should upgrade to avoid chain-tip synchronization stalls, especially in networks where nodes frequently connect at equal heights. No immediate incident response is indicated.
Security signals we found
Denial-of-service-like synchronization stall
Network consensus/partitioning risk from nodes failing to advance chain tip
Logic bug in peer-state guard condition
Evidence from the diff
In netsync/manager.go, handleInvMsg previously early-returned for any inv message from a peer that was not sm.syncPeer whenever sm.current() was false. The intent was to avoid fetching orphan blocks. However, if no syncPeer was set (e.g., two nodes connect at equal heights and startSync exits without choosing one), this guard dropped all block announcements and left the node stuck. The patch adds a sm.syncPeer != nil check so the early return only applies when a syncPeer actually exists; otherwise the normal request path queues the announced blocks.
Changed components
netsync/manager.gohandleInvMsgSyncManager.syncPeer handlingInspect captured patch +4 / −3
diff --git a/netsync/manager.go b/netsync/manager.go
index cf9c898..9addc5c 100644
--- a/netsync/manager.go
+++ b/netsync/manager.go
@@ -1124,9 +1124,10 @@ func (sm *SyncManager) handleInvMsg(imsg *invMsg) {
peer.UpdateLastAnnouncedBlock(&invVects[lastBlock].Hash)
}
- // Ignore invs from peers that aren't the sync if we are not current.
- // Helps prevent fetching a mass of orphans.
- if peer != sm.syncPeer && !sm.current() {
+ // Ignore invs from peers that aren't the sync peer if we are not
+ // current. Helps prevent fetching a mass of orphans. When syncPeer
+ // is nil, accept invs from any peer.
+ if sm.syncPeer != nil && peer != sm.syncPeer && !sm.current() {
return
}
Why this scored 47/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.