Skip to content

Commit 1e78f56

Browse files
committed
net: add NetEventsInterface::g_msgproc_mutex
There are many cases where we assume message processing is single-threaded in order for how we access node-related memory to be safe. Add an explicit mutex that we can use to document this, which allows the compiler to catch any cases where we try to access that memory from other threads and break that assumption.
1 parent 124e75a commit 1e78f56

File tree

10 files changed

+35
-9
lines changed

10 files changed

+35
-9
lines changed

src/net.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,8 +1978,12 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
19781978
}
19791979
}
19801980

1981+
Mutex NetEventsInterface::g_msgproc_mutex;
1982+
19811983
void CConnman::ThreadMessageHandler()
19821984
{
1985+
LOCK(NetEventsInterface::g_msgproc_mutex);
1986+
19831987
SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER);
19841988
while (!flagInterruptMsgProc)
19851989
{

src/net.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ class CNode
629629
class NetEventsInterface
630630
{
631631
public:
632+
/** Mutex for anything that is only accessed via the msg processing thread */
633+
static Mutex g_msgproc_mutex;
634+
632635
/** Initialize a peer (setup state, queue any initial messages) */
633636
virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0;
634637

@@ -642,15 +645,15 @@ class NetEventsInterface
642645
* @param[in] interrupt Interrupt condition for processing threads
643646
* @return True if there is more work to be done
644647
*/
645-
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
648+
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
646649

647650
/**
648651
* Send queued protocol messages to a given node.
649652
*
650653
* @param[in] pnode The node which we are sending messages to.
651654
* @return True if there is more work to be done
652655
*/
653-
virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0;
656+
virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
654657

655658

656659
protected:

src/net_processing.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,9 @@ class PeerManagerImpl final : public PeerManager
515515
void InitializeNode(CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
516516
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
517517
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
518-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
518+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
519519
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
520-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
520+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex);
521521

522522
/** Implement PeerManager */
523523
void StartScheduledTasks(CScheduler& scheduler) override;
@@ -532,7 +532,7 @@ class PeerManagerImpl final : public PeerManager
532532
void UnitTestMisbehaving(NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), howmuch, ""); };
533533
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
534534
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
535-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
535+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
536536
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
537537

538538
private:
@@ -3135,6 +3135,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
31353135
const std::chrono::microseconds time_received,
31363136
const std::atomic<bool>& interruptMsgProc)
31373137
{
3138+
AssertLockHeld(g_msgproc_mutex);
3139+
31383140
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
31393141

31403142
PeerRef peer = GetPeerRef(pfrom.GetId());
@@ -4748,6 +4750,8 @@ bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
47484750

47494751
bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
47504752
{
4753+
AssertLockHeld(g_msgproc_mutex);
4754+
47514755
bool fMoreWork = false;
47524756

47534757
PeerRef peer = GetPeerRef(pfrom->GetId());
@@ -5240,6 +5244,8 @@ bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer)
52405244

52415245
bool PeerManagerImpl::SendMessages(CNode* pto)
52425246
{
5247+
AssertLockHeld(g_msgproc_mutex);
5248+
52435249
PeerRef peer = GetPeerRef(pto->GetId());
52445250
if (!peer) return false;
52455251
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();

src/net_processing.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
8484

8585
/** Process a single message from a peer. Public for fuzz testing */
8686
virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
87-
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0;
87+
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
8888

8989
/** This function is used for testing the stale tip eviction logic, see denialofservice_tests.cpp */
9090
virtual void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) = 0;

src/test/denialofservice_tests.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
4545
// work.
4646
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
4747
{
48+
LOCK(NetEventsInterface::g_msgproc_mutex);
49+
4850
ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
4951
// Disable inactivity checks for this test to avoid interference
5052
connman.SetPeerConnectTimeout(99999s);
@@ -274,6 +276,8 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
274276

275277
BOOST_AUTO_TEST_CASE(peer_discouragement)
276278
{
279+
LOCK(NetEventsInterface::g_msgproc_mutex);
280+
277281
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
278282
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
279283
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
@@ -386,6 +390,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
386390

387391
BOOST_AUTO_TEST_CASE(DoS_bantime)
388392
{
393+
LOCK(NetEventsInterface::g_msgproc_mutex);
394+
389395
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
390396
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
391397
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),

src/test/fuzz/process_message.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE
7373
SetMockTime(1610000000); // any time to successfully reset ibd
7474
chainstate.ResetIbd();
7575

76+
LOCK(NetEventsInterface::g_msgproc_mutex);
77+
7678
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
7779
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
7880
return;

src/test/fuzz/process_messages.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
4040
SetMockTime(1610000000); // any time to successfully reset ibd
4141
chainstate.ResetIbd();
4242

43+
LOCK(NetEventsInterface::g_msgproc_mutex);
44+
4345
std::vector<CNode*> peers;
4446
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
4547
for (int i = 0; i < num_peers_to_add; ++i) {

src/test/fuzz/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
328328
}
329329
inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, node_id_in); }
330330

331-
void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept;
331+
void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
332332

333333
class FuzzedFileProvider
334334
{

src/test/net_tests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,8 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
805805

806806
BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
807807
{
808+
LOCK(NetEventsInterface::g_msgproc_mutex);
809+
808810
// Tests the following scenario:
809811
// * -bind=3.4.5.6:20001 is specified
810812
// * we make an outbound connection to a peer

src/test/util/net.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ struct ConnmanTestMsg : public CConnman {
4444
ServiceFlags remote_services,
4545
ServiceFlags local_services,
4646
int32_t version,
47-
bool relay_txs);
47+
bool relay_txs)
48+
EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
4849

49-
void ProcessMessagesOnce(CNode& node) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); }
50+
void ProcessMessagesOnce(CNode& node) EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); }
5051

5152
void NodeReceiveMsgBytes(CNode& node, Span<const uint8_t> msg_bytes, bool& complete) const;
5253

0 commit comments

Comments
 (0)