Skip to content

Commit 8db506f

Browse files
glozowTom Trevethan
authored andcommitted
Merge bitcoin/bitcoin#30394: net: fix race condition in self-connect detection
16bd283 Reapply "test: p2p: check that connecting to ourself leads to disconnect" (Sebastian Falbesoner) 0dbcd4c net: prevent sending messages in `NetEventsInterface::InitializeNode` (Sebastian Falbesoner) 66673f1 net: fix race condition in self-connect detection (Sebastian Falbesoner) Pull request description: This PR fixes a recently discovered race condition in the self-connect detection (see #30362 and #30368). Initiating an outbound network connection currently involves the following steps after the socket connection is established (see [`CConnman::OpenNetworkConnection`](https://github.com/bitcoin/bitcoin/blob/bd5d1688b4311e21c0e0ff89a3ae02ef7d0543b8/src/net.cpp#L2923-L2930) method): 1. set up node state 2. queue VERSION message (both steps 1 and 2 happen in [`InitializeNode`](https://github.com/bitcoin/bitcoin/blob/bd5d1688b4311e21c0e0ff89a3ae02ef7d0543b8/src/net_processing.cpp#L1662-L1683)) 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`. The temporarily reverted test introduced in #30362 is readded. Fixes #30368. Thanks go to vasild, mzumsande and dergoegge for suggestions on how to fix this (see bitcoin/bitcoin#30368 (comment) ff. and bitcoin/bitcoin#30394 (comment)). ACKs for top commit: naiyoma: tested ACK [https://github.com/bitcoin/bitcoin/pull/30394/commits/16bd283b3ad05daa41259a062aee0fc05b463fa6](https://github.com/bitcoin/bitcoin/pull/30394/commits/16bd283b3ad05daa41259a062aee0fc05b463fa6), built and tested locally, test passes successfully. mzumsande: ACK 16bd283 tdb3: ACK 16bd283 glozow: ACK 16bd283 dergoegge: ACK 16bd283 Tree-SHA512: 5b8aced6cda8deb38d4cd3fe4980b8af505d37ffa0925afaa734c5d81efe9d490dc48a42e1d0d45dd2961c0e1172a3d5b6582ae9a2d642f2592a17fbdc184445
1 parent 956797f commit 8db506f

File tree

3 files changed

+21
-9
lines changed

3 files changed

+21
-9
lines changed

src/net.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ class NetEventsInterface
727727
{
728728
public:
729729
/** Initialize a peer (setup state, queue any initial messages) */
730-
virtual void InitializeNode(CNode* pnode) = 0;
730+
virtual void InitializeNode(const CNode* pnode) = 0;
731731

732732
/** Handle removal of a peer (clear state) */
733733
virtual void FinalizeNode(const CNode& node) = 0;

src/net_processing.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ struct Peer {
222222
* Most peers use headers-first syncing, which doesn't use this mechanism */
223223
uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {};
224224

225+
Mutex m_msgproc_mutex;
226+
/** Set to true once initial VERSION message was sent (only relevant for outbound peers). */
227+
bool m_outbound_version_message_sent GUARDED_BY(m_msgproc_mutex){false};
228+
225229
/** This peer's reported block height when we connected */
226230
std::atomic<int> m_starting_height{-1};
227231

@@ -312,7 +316,7 @@ class PeerManagerImpl final : public PeerManager
312316
void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
313317

314318
/** Implement NetEventsInterface */
315-
void InitializeNode(CNode* pnode) override;
319+
void InitializeNode(const CNode* pnode) override;
316320
void FinalizeNode(const CNode& node) override;
317321
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
318322
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
@@ -1189,7 +1193,7 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
11891193
if (state) state->m_last_block_announcement = time_in_seconds;
11901194
}
11911195

1192-
void PeerManagerImpl::InitializeNode(CNode *pnode)
1196+
void PeerManagerImpl::InitializeNode(const CNode *pnode)
11931197
{
11941198
NodeId nodeid = pnode->GetId();
11951199
{
@@ -1202,9 +1206,6 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
12021206
LOCK(m_peer_mutex);
12031207
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, std::move(peer));
12041208
}
1205-
if (!pnode->IsInboundConn()) {
1206-
PushNodeVersion(*pnode);
1207-
}
12081209
}
12091210

12101211
void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
@@ -4201,6 +4202,10 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
42014202
PeerRef peer = GetPeerRef(pfrom->GetId());
42024203
if (peer == nullptr) return false;
42034204

4205+
// For outbound connections, ensure that the initial VERSION message
4206+
// has been sent first before processing any incoming messages
4207+
if (!pfrom->IsInboundConn() && WITH_LOCK(peer->m_msgproc_mutex, return !peer->m_outbound_version_message_sent)) return false;
4208+
42044209
{
42054210
LOCK(peer->m_getdata_requests_mutex);
42064211
if (!peer->m_getdata_requests.empty()) {
@@ -4660,6 +4665,13 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
46604665
// disconnect misbehaving peers even before the version handshake is complete.
46614666
if (MaybeDiscourageAndDisconnect(*pto, *peer)) return true;
46624667

4668+
// Initiate version handshake for outbound connections
4669+
if (!pto->IsInboundConn() && WITH_LOCK(peer->m_msgproc_mutex, return !peer->m_outbound_version_message_sent)) {
4670+
LOCK(peer->m_msgproc_mutex);
4671+
PushNodeVersion(*pto);
4672+
peer->m_outbound_version_message_sent = true;
4673+
}
4674+
46634675
// Don't send anything until the version handshake is complete
46644676
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
46654677
return true;

src/test/fuzz/util.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,13 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman,
244244
filter_txs),
245245
};
246246

247-
(void)connman.ReceiveMsgFrom(node, msg_version);
248-
node.fPauseSend = false;
249-
connman.ProcessMessagesOnce(node);
250247
{
251248
LOCK(node.cs_sendProcessing);
252249
peerman.SendMessages(&node);
253250
}
251+
(void)connman.ReceiveMsgFrom(node, msg_version);
252+
node.fPauseSend = false;
253+
connman.ProcessMessagesOnce(node);
254254
if (node.fDisconnect) return;
255255
assert(node.nVersion == version);
256256
assert(node.GetCommonVersion() == std::min(version, PROTOCOL_VERSION));

0 commit comments

Comments
 (0)