Skip to content

Commit 1987e88

Browse files
UdjinM6kwvg
authored andcommitted
coinjoin: protect m_wallet_manager_map with cs_wallet_manager_map
Avoid TSan-reported data race ``` WARNING: ThreadSanitizer: data race (pid=374820) Read of size 8 at 0x7b140002ce10 by thread T12: #0 _M_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:154:42 (dashd+0xb58e08) (BuildId: c3fdce9f7e778985a4fb0968ff4506d9ad24d408) dashpay#1 get /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:361:21 (dashd+0xb58e08) dashpay#2 operator-> /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:355:9 (dashd+0xb58e08) dashpay#3 CoinJoinWalletManager::DoMaintenance() /src/dash/src/coinjoin/client.cpp:1907:9 (dashd+0xb58e08) [...] Previous write of size 8 at 0x7b140002ce10 by thread T17 (mutexes: write M0): #0 operator new(unsigned long) <null> (dashd+0x162657) (BuildId: c3fdce9f7e778985a4fb0968ff4506d9ad24d408) dashpay#1 allocate /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:114:27 (dashd+0xb772b4) (BuildId: c3fdce9f7e778985a4fb0968ff4506d9ad24d408) dashpay#2 allocate /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/alloc_traits.h:443:20 (dashd+0xb772b4) dashpay#3 _M_get_node /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_tree.h:580:16 (dashd+0xb772b4) [...] dashpay#8 CoinJoinWalletManager::Add(CWallet&) /src/dash/src/coinjoin/client.cpp:1898:26 (dashd+0xb58c73) (BuildId: c3fdce9f7e778985a4fb0968ff4506d9ad24d408) [...] SUMMARY: ThreadSanitizer: data race [...] ```
1 parent 045e178 commit 1987e88

File tree

6 files changed

+76
-44
lines changed

6 files changed

+76
-44
lines changed

src/coinjoin/client.cpp

+39-28
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,9 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CDataS
9696
}
9797

9898
// if the queue is ready, submit if we can
99-
if (dsq.fReady && ranges::any_of(m_walletman.raw(),
100-
[this, &dmn](const auto &pair) {
101-
return pair.second->TrySubmitDenominate(dmn->pdmnState->addr,
102-
this->connman);
103-
})) {
99+
if (dsq.fReady && m_walletman.ForAnyCJClientMan([this, &dmn](std::unique_ptr<CCoinJoinClientManager>& clientman) {
100+
return clientman->TrySubmitDenominate(dmn->pdmnState->addr, this->connman);
101+
})) {
104102
LogPrint(BCLog::COINJOIN, "DSQUEUE -- CoinJoin queue (%s) is ready on masternode %s\n", dsq.ToString(),
105103
dmn->pdmnState->addr.ToString());
106104
return {};
@@ -121,8 +119,9 @@ PeerMsgRet CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, CDataS
121119
LogPrint(BCLog::COINJOIN, "DSQUEUE -- new CoinJoin queue (%s) from masternode %s\n", dsq.ToString(),
122120
dmn->pdmnState->addr.ToString());
123121

124-
ranges::any_of(m_walletman.raw(),
125-
[&dsq](const auto &pair) { return pair.second->MarkAlreadyJoinedQueueAsTried(dsq); });
122+
m_walletman.ForAnyCJClientMan([&dsq](const std::unique_ptr<CCoinJoinClientManager>& clientman) {
123+
return clientman->MarkAlreadyJoinedQueueAsTried(dsq);
124+
});
126125

127126
WITH_LOCK(cs_vecqueue, vecCoinJoinQueue.push_back(dsq));
128127
}
@@ -155,11 +154,14 @@ void CCoinJoinClientManager::ProcessMessage(CNode& peer, CChainState& active_cha
155154
}
156155
}
157156

158-
CCoinJoinClientSession::CCoinJoinClientSession(CWallet& wallet, CoinJoinWalletManager& walletman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
159-
const CMasternodeSync& mn_sync, const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode) :
157+
CCoinJoinClientSession::CCoinJoinClientSession(CWallet& wallet, CoinJoinWalletManager& walletman,
158+
CCoinJoinClientManager& clientman, CDeterministicMNManager& dmnman,
159+
CMasternodeMetaMan& mn_metaman, const CMasternodeSync& mn_sync,
160+
const std::unique_ptr<CCoinJoinClientQueueManager>& queueman,
161+
bool is_masternode) :
160162
m_wallet(wallet),
161163
m_walletman(walletman),
162-
m_manager(*Assert(walletman.Get(wallet.GetName()))),
164+
m_clientman(clientman),
163165
m_dmnman(dmnman),
164166
m_mn_metaman(mn_metaman),
165167
m_mn_sync(mn_sync),
@@ -684,7 +686,7 @@ void CCoinJoinClientSession::CompletedTransaction(PoolMessage nMessageID)
684686
if (m_is_masternode) return;
685687

686688
if (nMessageID == MSG_SUCCESS) {
687-
m_manager.UpdatedSuccessBlock();
689+
m_clientman.UpdatedSuccessBlock();
688690
keyHolderStorage.KeepAll();
689691
WalletCJLogPrint(m_wallet, "CompletedTransaction -- success\n");
690692
} else {
@@ -995,7 +997,8 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(CChainState& active_chainst
995997
AssertLockNotHeld(cs_deqsessions);
996998
LOCK(cs_deqsessions);
997999
if (int(deqSessions.size()) < CCoinJoinClientOptions::GetSessions()) {
998-
deqSessions.emplace_back(m_wallet, m_walletman, m_dmnman, m_mn_metaman, m_mn_sync, m_queueman, m_is_masternode);
1000+
deqSessions.emplace_back(m_wallet, m_walletman, *this, m_dmnman, m_mn_metaman, m_mn_sync, m_queueman,
1001+
m_is_masternode);
9991002
}
10001003
for (auto& session : deqSessions) {
10011004
if (!CheckAutomaticBackup()) return false;
@@ -1100,7 +1103,7 @@ bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized,
11001103
continue;
11011104
}
11021105

1103-
m_manager.AddUsedMasternode(dsq.masternodeOutpoint);
1106+
m_clientman.AddUsedMasternode(dsq.masternodeOutpoint);
11041107

11051108
if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->addr)) {
11061109
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::JoinExistingQueue -- skipping masternode connection, addr=%s\n", dmn->pdmnState->addr.ToString());
@@ -1145,14 +1148,14 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon
11451148

11461149
// otherwise, try one randomly
11471150
while (nTries < 10) {
1148-
auto dmn = m_manager.GetRandomNotUsedMasternode();
1151+
auto dmn = m_clientman.GetRandomNotUsedMasternode();
11491152
if (!dmn) {
11501153
strAutoDenomResult = _("Can't find random Masternode.");
11511154
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::StartNewQueue -- %s\n", strAutoDenomResult.original);
11521155
return false;
11531156
}
11541157

1155-
m_manager.AddUsedMasternode(dmn->collateralOutpoint);
1158+
m_clientman.AddUsedMasternode(dmn->collateralOutpoint);
11561159

11571160
// skip next mn payments winners
11581161
if (dmn->pdmnState->nLastPaidHeight + nWeightedMnCount < mnList.GetHeight() + WinnersToSkip()) {
@@ -1526,7 +1529,7 @@ bool CCoinJoinClientSession::MakeCollateralAmounts(const CompactTallyItem& tally
15261529
return false;
15271530
}
15281531

1529-
m_manager.UpdatedSuccessBlock();
1532+
m_clientman.UpdatedSuccessBlock();
15301533

15311534
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::%s -- txid: %s\n", __func__, strResult.original);
15321535

@@ -1803,7 +1806,7 @@ bool CCoinJoinClientSession::CreateDenominated(CAmount nBalanceToDenominate, con
18031806
}
18041807

18051808
// use the same nCachedLastSuccessBlock as for DS mixing to prevent race
1806-
m_manager.UpdatedSuccessBlock();
1809+
m_clientman.UpdatedSuccessBlock();
18071810

18081811
WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::%s -- txid: %s\n", __func__, strResult.original);
18091812

@@ -1894,35 +1897,43 @@ void CCoinJoinClientManager::GetJsonInfo(UniValue& obj) const
18941897
obj.pushKV("sessions", arrSessions);
18951898
}
18961899

1897-
void CoinJoinWalletManager::Add(CWallet& wallet) {
1898-
m_wallet_manager_map.try_emplace(
1899-
wallet.GetName(),
1900-
std::make_unique<CCoinJoinClientManager>(wallet, *this, m_dmnman, m_mn_metaman, m_mn_sync, m_queueman, m_is_masternode)
1901-
);
1900+
void CoinJoinWalletManager::Add(CWallet& wallet)
1901+
{
1902+
{
1903+
LOCK(cs_wallet_manager_map);
1904+
m_wallet_manager_map.try_emplace(wallet.GetName(),
1905+
std::make_unique<CCoinJoinClientManager>(wallet, *this, m_dmnman, m_mn_metaman,
1906+
m_mn_sync, m_queueman, m_is_masternode));
1907+
}
19021908
g_wallet_init_interface.InitCoinJoinSettings(*this);
19031909
}
19041910

1905-
void CoinJoinWalletManager::DoMaintenance() {
1906-
for (auto& [wallet_str, walletman] : m_wallet_manager_map) {
1907-
walletman->DoMaintenance(m_chainstate, m_connman, m_mempool);
1911+
void CoinJoinWalletManager::DoMaintenance()
1912+
{
1913+
LOCK(cs_wallet_manager_map);
1914+
for (auto& [_, clientman] : m_wallet_manager_map) {
1915+
clientman->DoMaintenance(m_chainstate, m_connman, m_mempool);
19081916
}
19091917
}
19101918

19111919
void CoinJoinWalletManager::Remove(const std::string& name) {
1912-
m_wallet_manager_map.erase(name);
1920+
{
1921+
LOCK(cs_wallet_manager_map);
1922+
m_wallet_manager_map.erase(name);
1923+
}
19131924
g_wallet_init_interface.InitCoinJoinSettings(*this);
19141925
}
19151926

19161927
void CoinJoinWalletManager::Flush(const std::string& name)
19171928
{
1918-
auto clientman = Get(name);
1919-
assert(clientman != nullptr);
1929+
auto clientman = Assert(Get(name));
19201930
clientman->ResetPool();
19211931
clientman->StopMixing();
19221932
}
19231933

19241934
CCoinJoinClientManager* CoinJoinWalletManager::Get(const std::string& name) const
19251935
{
1936+
LOCK(cs_wallet_manager_map);
19261937
auto it = m_wallet_manager_map.find(name);
19271938
return (it != m_wallet_manager_map.end()) ? it->second.get() : nullptr;
19281939
}

src/coinjoin/client.h

+27-5
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class CoinJoinWalletManager {
8080
{}
8181

8282
~CoinJoinWalletManager() {
83+
LOCK(cs_wallet_manager_map);
8384
for (auto& [wallet_name, cj_man] : m_wallet_manager_map) {
8485
cj_man.reset();
8586
}
@@ -93,7 +94,24 @@ class CoinJoinWalletManager {
9394

9495
CCoinJoinClientManager* Get(const std::string& name) const;
9596

96-
const wallet_name_cjman_map& raw() const { return m_wallet_manager_map; }
97+
template <typename Callable>
98+
void ForEachCJClientMan(Callable&& func)
99+
{
100+
LOCK(cs_wallet_manager_map);
101+
for (auto&& [_, clientman] : m_wallet_manager_map) {
102+
func(clientman);
103+
}
104+
};
105+
106+
template <typename Callable>
107+
bool ForAnyCJClientMan(Callable&& func)
108+
{
109+
LOCK(cs_wallet_manager_map);
110+
for (auto&& [_, clientman] : m_wallet_manager_map) {
111+
if (func(clientman)) return true;
112+
}
113+
return false;
114+
};
97115

98116
private:
99117
CChainState& m_chainstate;
@@ -105,15 +123,17 @@ class CoinJoinWalletManager {
105123
const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
106124

107125
const bool m_is_masternode;
108-
wallet_name_cjman_map m_wallet_manager_map;
126+
127+
mutable Mutex cs_wallet_manager_map;
128+
wallet_name_cjman_map m_wallet_manager_map GUARDED_BY(cs_wallet_manager_map);
109129
};
110130

111131
class CCoinJoinClientSession : public CCoinJoinBaseSession
112132
{
113133
private:
114134
CWallet& m_wallet;
115135
CoinJoinWalletManager& m_walletman;
116-
CCoinJoinClientManager& m_manager;
136+
CCoinJoinClientManager& m_clientman;
117137
CDeterministicMNManager& m_dmnman;
118138
CMasternodeMetaMan& m_mn_metaman;
119139
const CMasternodeSync& m_mn_sync;
@@ -168,8 +188,10 @@ class CCoinJoinClientSession : public CCoinJoinBaseSession
168188
void SetNull() override EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
169189

170190
public:
171-
explicit CCoinJoinClientSession(CWallet& wallet, CoinJoinWalletManager& walletman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
172-
const CMasternodeSync& mn_sync, const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode);
191+
explicit CCoinJoinClientSession(CWallet& wallet, CoinJoinWalletManager& walletman,
192+
CCoinJoinClientManager& clientman, CDeterministicMNManager& dmnman,
193+
CMasternodeMetaMan& mn_metaman, const CMasternodeSync& mn_sync,
194+
const std::unique_ptr<CCoinJoinClientQueueManager>& queueman, bool is_masternode);
173195

174196
void ProcessMessage(CNode& peer, CChainState& active_chainstate, CConnman& connman, const CTxMemPool& mempool, std::string_view msg_type, CDataStream& vRecv);
175197

src/dsnotificationinterface.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
8686

8787
m_cj_ctx->dstxman->UpdatedBlockTip(pindexNew, *m_llmq_ctx->clhandler, m_mn_sync);
8888
#ifdef ENABLE_WALLET
89-
for (auto& pair : m_cj_ctx->walletman->raw()) {
90-
pair.second->UpdatedBlockTip(pindexNew);
91-
}
89+
m_cj_ctx->walletman->ForEachCJClientMan(
90+
[&pindexNew](std::unique_ptr<CCoinJoinClientManager>& clientman) { clientman->UpdatedBlockTip(pindexNew); });
9291
#endif // ENABLE_WALLET
9392

9493
m_llmq_ctx->isman->UpdatedBlockTip(pindexNew);

src/masternode/utils.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ void CMasternodeUtils::DoMaintenance(CConnman& connman, CDeterministicMNManager&
2323

2424
std::vector<CDeterministicMNCPtr> vecDmns; // will be empty when no wallet
2525
#ifdef ENABLE_WALLET
26-
for (auto& pair : cj_ctx.walletman->raw()) {
27-
pair.second->GetMixingMasternodesInfo(vecDmns);
28-
}
26+
cj_ctx.walletman->ForEachCJClientMan([&vecDmns](const std::unique_ptr<CCoinJoinClientManager>& clientman) {
27+
clientman->GetMixingMasternodesInfo(vecDmns);
28+
});
2929
#endif // ENABLE_WALLET
3030

3131
// Don't disconnect masternode connections when we have less then the desired amount of outbound nodes

src/net_processing.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -5095,9 +5095,9 @@ void PeerManagerImpl::ProcessMessage(
50955095
//probably one the extensions
50965096
#ifdef ENABLE_WALLET
50975097
ProcessPeerMsgRet(m_cj_ctx->queueman->ProcessMessage(pfrom, msg_type, vRecv), pfrom);
5098-
for (auto& pair : m_cj_ctx->walletman->raw()) {
5099-
pair.second->ProcessMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv);
5100-
}
5098+
m_cj_ctx->walletman->ForEachCJClientMan([this, &pfrom, &msg_type, &vRecv](std::unique_ptr<CCoinJoinClientManager>& clientman) {
5099+
clientman->ProcessMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv);
5100+
});
51015101
#endif // ENABLE_WALLET
51025102
ProcessPeerMsgRet(m_cj_ctx->server->ProcessMessage(pfrom, msg_type, vRecv), pfrom);
51035103
ProcessPeerMsgRet(m_sporkman.ProcessMessage(pfrom, m_connman, *this, msg_type, vRecv), pfrom);

src/wallet/test/coinjoin_tests.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ class CTransactionBuilderTestSetup : public TestChain100Setup
208208

209209
BOOST_FIXTURE_TEST_CASE(coinjoin_manager_start_stop_tests, CTransactionBuilderTestSetup)
210210
{
211-
BOOST_CHECK_EQUAL(m_node.cj_ctx->walletman->raw().size(), 1);
212-
auto& cj_man = m_node.cj_ctx->walletman->raw().begin()->second;
211+
CCoinJoinClientManager* cj_man = m_node.cj_ctx->walletman->Get("");
212+
BOOST_ASSERT(cj_man != nullptr);
213213
BOOST_CHECK_EQUAL(cj_man->IsMixing(), false);
214214
BOOST_CHECK_EQUAL(cj_man->StartMixing(), true);
215215
BOOST_CHECK_EQUAL(cj_man->IsMixing(), true);

0 commit comments

Comments
 (0)