Skip to content

Commit 0933cf5

Browse files
theStackfanquake
authored andcommitted
net: fix race condition in self-connect detection
Initiating an outbound network connection currently involves the following steps after the socket connection is established (see `CConnman::OpenNetworkConnection` method): 1. set up node state 2. queue VERSION message 3. add new node to vector `m_nodes` If we connect to ourself, it can happen that the sent VERSION message (step 2) is received and processed locally *before* the node object is added to the connection manager's `m_nodes` vector (step 3). In this case, the self-connect remains undiscovered, as the detection doesn't find the outbound peer in `m_nodes` yet (see `CConnman::CheckIncomingNonce`). Fix this by swapping the order of 2. and 3., by taking the `PushNodeVersion` call out of `InitializeNode` and doing that in the `SendMessages` method instead, which is only called for `CNode` instances in `m_nodes`. Thanks go to vasild, mzumsande, dergoegge and sipa for suggestions on how to fix this. Github-Pull: #30394 Rebased-From: 66673f1
1 parent fa90989 commit 0933cf5

File tree

3 files changed

+16
-5
lines changed

3 files changed

+16
-5
lines changed

src/net.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ class NetEventsInterface
999999
/** Mutex for anything that is only accessed via the msg processing thread */
10001000
static Mutex g_msgproc_mutex;
10011001

1002-
/** Initialize a peer (setup state, queue any initial messages) */
1002+
/** Initialize a peer (setup state) */
10031003
virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0;
10041004

10051005
/** Handle removal of a peer (clear state) */

src/net_processing.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ struct Peer {
245245
* Most peers use headers-first syncing, which doesn't use this mechanism */
246246
uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {};
247247

248+
/** Set to true once initial VERSION message was sent (only relevant for outbound peers). */
249+
bool m_outbound_version_message_sent GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
250+
248251
/** This peer's reported block height when we connected */
249252
std::atomic<int> m_starting_height{-1};
250253

@@ -1576,9 +1579,6 @@ void PeerManagerImpl::InitializeNode(CNode& node, ServiceFlags our_services)
15761579
LOCK(m_peer_mutex);
15771580
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, peer);
15781581
}
1579-
if (!node.IsInboundConn()) {
1580-
PushNodeVersion(node, *peer);
1581-
}
15821582
}
15831583

15841584
void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
@@ -5060,6 +5060,10 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
50605060
PeerRef peer = GetPeerRef(pfrom->GetId());
50615061
if (peer == nullptr) return false;
50625062

5063+
// For outbound connections, ensure that the initial VERSION message
5064+
// has been sent first before processing any incoming messages
5065+
if (!pfrom->IsInboundConn() && !peer->m_outbound_version_message_sent) return false;
5066+
50635067
{
50645068
LOCK(peer->m_getdata_requests_mutex);
50655069
if (!peer->m_getdata_requests.empty()) {
@@ -5548,6 +5552,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
55485552
// disconnect misbehaving peers even before the version handshake is complete.
55495553
if (MaybeDiscourageAndDisconnect(*pto, *peer)) return true;
55505554

5555+
// Initiate version handshake for outbound connections
5556+
if (!pto->IsInboundConn() && !peer->m_outbound_version_message_sent) {
5557+
PushNodeVersion(*pto, *peer);
5558+
peer->m_outbound_version_message_sent = true;
5559+
}
5560+
55515561
// Don't send anything until the version handshake is complete
55525562
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
55535563
return true;

src/test/util/net.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ void ConnmanTestMsg::Handshake(CNode& node,
2828
auto& connman{*this};
2929

3030
peerman.InitializeNode(node, local_services);
31-
FlushSendBuffer(node); // Drop the version message added by InitializeNode.
31+
peerman.SendMessages(&node);
32+
FlushSendBuffer(node); // Drop the version message added by SendMessages.
3233

3334
CSerializedNetMsg msg_version{
3435
NetMsg::Make(NetMsgType::VERSION,

0 commit comments

Comments
 (0)