aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Falbesoner <sebastian.falbesoner@gmail.com>2024-07-04 20:12:13 +0200
committerSebastian Falbesoner <sebastian.falbesoner@gmail.com>2024-07-09 21:35:53 +0200
commit66673f1c1302c986e344c7f44bb0b352213d5dc8 (patch)
treee7ac85185907a622643216885529b7a0a836d343
parent1c11089c7f11d3fe6e6dcf856c1330a1721048cf (diff)
downloadbitcoin-66673f1c1302c986e344c7f44bb0b352213d5dc8.tar.xz
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.
-rw-r--r--src/net.h2
-rw-r--r--src/net_processing.cpp16
-rw-r--r--src/test/util/net.cpp3
3 files changed, 16 insertions, 5 deletions
diff --git a/src/net.h b/src/net.h
index 8075975d62..050af0d5ac 100644
--- a/src/net.h
+++ b/src/net.h
@@ -991,7 +991,7 @@ public:
/** Mutex for anything that is only accessed via the msg processing thread */
static Mutex g_msgproc_mutex;
- /** Initialize a peer (setup state, queue any initial messages) */
+ /** Initialize a peer (setup state) */
virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0;
/** Handle removal of a peer (clear state) */
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 8137e17c98..080d6241c9 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -243,6 +243,9 @@ struct Peer {
* Most peers use headers-first syncing, which doesn't use this mechanism */
uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {};
+ /** Set to true once initial VERSION message was sent (only relevant for outbound peers). */
+ bool m_outbound_version_message_sent GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
+
/** This peer's reported block height when we connected */
std::atomic<int> m_starting_height{-1};
@@ -1677,9 +1680,6 @@ void PeerManagerImpl::InitializeNode(CNode& node, ServiceFlags our_services)
LOCK(m_peer_mutex);
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, peer);
}
- if (!node.IsInboundConn()) {
- PushNodeVersion(node, *peer);
- }
}
void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
@@ -5326,6 +5326,10 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
PeerRef peer = GetPeerRef(pfrom->GetId());
if (peer == nullptr) return false;
+ // For outbound connections, ensure that the initial VERSION message
+ // has been sent first before processing any incoming messages
+ if (!pfrom->IsInboundConn() && !peer->m_outbound_version_message_sent) return false;
+
{
LOCK(peer->m_getdata_requests_mutex);
if (!peer->m_getdata_requests.empty()) {
@@ -5817,6 +5821,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// disconnect misbehaving peers even before the version handshake is complete.
if (MaybeDiscourageAndDisconnect(*pto, *peer)) return true;
+ // Initiate version handshake for outbound connections
+ if (!pto->IsInboundConn() && !peer->m_outbound_version_message_sent) {
+ PushNodeVersion(*pto, *peer);
+ peer->m_outbound_version_message_sent = true;
+ }
+
// Don't send anything until the version handshake is complete
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
return true;
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 9b38d85d58..e4b2ac5743 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -28,7 +28,8 @@ void ConnmanTestMsg::Handshake(CNode& node,
auto& connman{*this};
peerman.InitializeNode(node, local_services);
- FlushSendBuffer(node); // Drop the version message added by InitializeNode.
+ peerman.SendMessages(&node);
+ FlushSendBuffer(node); // Drop the version message added by SendMessages.
CSerializedNetMsg msg_version{
NetMsg::Make(NetMsgType::VERSION,