Skip to content

Commit a06ede9

Browse files
committed
Merge #9708: Clean up all known races/platform-specific UB at the time PR was opened
db2dc7a Move CNode::addrLocal access behind locked accessors (Matt Corallo) 036073b Move CNode::addrName accesses behind locked accessors (Matt Corallo) d8f2b8a Make nTimeBestReceived atomic (Matt Corallo) 22b4966 Move [clean|str]SubVer writes/copyStats into a lock (Matt Corallo) 0f31872 Make nServices atomic (Matt Corallo) 96f42d8 Make nStartingHeight atomic (Matt Corallo) 512731b Access fRelayTxes with cs_filter lock in copyStats (Matt Corallo) ae683c1 Avoid copying CNodeStats to make helgrind OK with buggy std::string (Matt Corallo) 644f123 Make nTimeConnected const in CNode (Matt Corallo) 321d0fc net: fix a few races. Credit @TheBlueMatt (Cory Fields)
2 parents b860915 + db2dc7a commit a06ede9

File tree

3 files changed

+105
-45
lines changed

3 files changed

+105
-45
lines changed

src/net.cpp

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,9 @@ int GetnScore(const CService& addr)
164164
// Is our peer's addrLocal potentially useful as an external IP source?
165165
bool IsPeerAddrLocalGood(CNode *pnode)
166166
{
167-
return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() &&
168-
!IsLimited(pnode->addrLocal.GetNetwork());
167+
CService addrLocal = pnode->GetAddrLocal();
168+
return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() &&
169+
!IsLimited(addrLocal.GetNetwork());
169170
}
170171

171172
// pushes our own address to a peer
@@ -180,7 +181,7 @@ void AdvertiseLocal(CNode *pnode)
180181
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
181182
GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
182183
{
183-
addrLocal.SetIP(pnode->addrLocal);
184+
addrLocal.SetIP(pnode->GetAddrLocal());
184185
}
185186
if (addrLocal.IsRoutable())
186187
{
@@ -307,9 +308,11 @@ CNode* CConnman::FindNode(const CSubNet& subNet)
307308
CNode* CConnman::FindNode(const std::string& addrName)
308309
{
309310
LOCK(cs_vNodes);
310-
BOOST_FOREACH(CNode* pnode, vNodes)
311-
if (pnode->addrName == addrName)
311+
BOOST_FOREACH(CNode* pnode, vNodes) {
312+
if (pnode->GetAddrName() == addrName) {
312313
return (pnode);
314+
}
315+
}
313316
return NULL;
314317
}
315318

@@ -373,9 +376,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
373376
CNode* pnode = FindNode((CService)addrConnect);
374377
if (pnode)
375378
{
376-
if (pnode->addrName.empty()) {
377-
pnode->addrName = std::string(pszDest);
378-
}
379+
pnode->MaybeSetAddrName(std::string(pszDest));
379380
CloseSocket(hSocket);
380381
LogPrintf("Failed to open new connection, already connected\n");
381382
return NULL;
@@ -389,7 +390,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
389390
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
390391
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, pszDest ? pszDest : "", false);
391392
pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
392-
pnode->nTimeConnected = GetSystemTimeInSeconds();
393393
pnode->AddRef();
394394

395395
return pnode;
@@ -594,28 +594,67 @@ void CConnman::AddWhitelistedRange(const CSubNet &subnet) {
594594
vWhitelistedRange.push_back(subnet);
595595
}
596596

597+
598+
std::string CNode::GetAddrName() const {
599+
LOCK(cs_addrName);
600+
return addrName;
601+
}
602+
603+
void CNode::MaybeSetAddrName(const std::string& addrNameIn) {
604+
LOCK(cs_addrName);
605+
if (addrName.empty()) {
606+
addrName = addrNameIn;
607+
}
608+
}
609+
610+
CService CNode::GetAddrLocal() const {
611+
LOCK(cs_addrLocal);
612+
return addrLocal;
613+
}
614+
615+
void CNode::SetAddrLocal(const CService& addrLocalIn) {
616+
LOCK(cs_addrLocal);
617+
if (addrLocal.IsValid()) {
618+
error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString());
619+
} else {
620+
addrLocal = addrLocalIn;
621+
}
622+
}
623+
597624
#undef X
598625
#define X(name) stats.name = name
599626
void CNode::copyStats(CNodeStats &stats)
600627
{
601628
stats.nodeid = this->GetId();
602629
X(nServices);
603630
X(addr);
604-
X(fRelayTxes);
631+
{
632+
LOCK(cs_filter);
633+
X(fRelayTxes);
634+
}
605635
X(nLastSend);
606636
X(nLastRecv);
607637
X(nTimeConnected);
608638
X(nTimeOffset);
609-
X(addrName);
639+
stats.addrName = GetAddrName();
610640
X(nVersion);
611-
X(cleanSubVer);
641+
{
642+
LOCK(cs_SubVer);
643+
X(cleanSubVer);
644+
}
612645
X(fInbound);
613646
X(fAddnode);
614647
X(nStartingHeight);
615-
X(nSendBytes);
616-
X(mapSendBytesPerMsgCmd);
617-
X(nRecvBytes);
618-
X(mapRecvBytesPerMsgCmd);
648+
{
649+
LOCK(cs_vSend);
650+
X(mapSendBytesPerMsgCmd);
651+
X(nSendBytes);
652+
}
653+
{
654+
LOCK(cs_vRecv);
655+
X(mapRecvBytesPerMsgCmd);
656+
X(nRecvBytes);
657+
}
619658
X(fWhitelisted);
620659

621660
// It is common for nodes with good ping times to suddenly become lagged,
@@ -635,14 +674,16 @@ void CNode::copyStats(CNodeStats &stats)
635674
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
636675

637676
// Leave string empty if addrLocal invalid (not filled in yet)
638-
stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
677+
CService addrLocalUnlocked = GetAddrLocal();
678+
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
639679
}
640680
#undef X
641681

642682
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
643683
{
644684
complete = false;
645685
int64_t nTimeMicros = GetTimeMicros();
686+
LOCK(cs_vRecv);
646687
nLastRecv = nTimeMicros / 1000000;
647688
nRecvBytes += nBytes;
648689
while (nBytes > 0) {
@@ -1786,8 +1827,9 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
17861827
if (pnode->addr.IsValid()) {
17871828
mapConnected[pnode->addr] = pnode->fInbound;
17881829
}
1789-
if (!pnode->addrName.empty()) {
1790-
mapConnectedByName[pnode->addrName] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr));
1830+
std::string addrName = pnode->GetAddrName();
1831+
if (!addrName.empty()) {
1832+
mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr));
17911833
}
17921834
}
17931835
}
@@ -2414,9 +2456,8 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
24142456
vstats.reserve(vNodes.size());
24152457
for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) {
24162458
CNode* pnode = *it;
2417-
CNodeStats stats;
2418-
pnode->copyStats(stats);
2419-
vstats.push_back(stats);
2459+
vstats.emplace_back();
2460+
pnode->copyStats(vstats.back());
24202461
}
24212462
}
24222463

@@ -2568,6 +2609,7 @@ unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
25682609
unsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; }
25692610

25702611
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string& addrNameIn, bool fInboundIn) :
2612+
nTimeConnected(GetSystemTimeInSeconds()),
25712613
addr(addrIn),
25722614
fInbound(fInboundIn),
25732615
id(idIn),
@@ -2587,7 +2629,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
25872629
nLastRecv = 0;
25882630
nSendBytes = 0;
25892631
nRecvBytes = 0;
2590-
nTimeConnected = GetSystemTimeInSeconds();
25912632
nTimeOffset = 0;
25922633
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
25932634
nVersion = 0;

src/net.h

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ class CNode
564564
friend class CConnman;
565565
public:
566566
// socket
567-
ServiceFlags nServices;
567+
std::atomic<ServiceFlags> nServices;
568568
ServiceFlags nServicesExpected;
569569
SOCKET hSocket;
570570
size_t nSendSize; // total size of all vSendMsg entries
@@ -573,6 +573,7 @@ class CNode
573573
std::deque<std::vector<unsigned char>> vSendMsg;
574574
CCriticalSection cs_vSend;
575575
CCriticalSection cs_hSocket;
576+
CCriticalSection cs_vRecv;
576577

577578
CCriticalSection cs_vProcessMsg;
578579
std::list<CNetMessage> vProcessMsg;
@@ -584,19 +585,18 @@ class CNode
584585
uint64_t nRecvBytes;
585586
std::atomic<int> nRecvVersion;
586587

587-
int64_t nLastSend;
588-
int64_t nLastRecv;
589-
int64_t nTimeConnected;
590-
int64_t nTimeOffset;
588+
std::atomic<int64_t> nLastSend;
589+
std::atomic<int64_t> nLastRecv;
590+
const int64_t nTimeConnected;
591+
std::atomic<int64_t> nTimeOffset;
591592
const CAddress addr;
592-
std::string addrName;
593-
CService addrLocal;
594593
std::atomic<int> nVersion;
595594
// strSubVer is whatever byte array we read from the wire. However, this field is intended
596595
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
597596
// store the sanitized version in cleanSubVer. The original should be used when dealing with
598597
// the network or wire types and the cleaned string used when displayed or logged.
599598
std::string strSubVer, cleanSubVer;
599+
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
600600
bool fWhitelisted; // This peer can bypass DoS banning.
601601
bool fFeeler; // If true this node is being used as a short lived feeler.
602602
bool fOneShot;
@@ -614,7 +614,7 @@ class CNode
614614
CSemaphoreGrant grantOutbound;
615615
CCriticalSection cs_filter;
616616
CBloomFilter* pfilter;
617-
int nRefCount;
617+
std::atomic<int> nRefCount;
618618
const NodeId id;
619619

620620
const uint64_t nKeyedNetGroup;
@@ -627,7 +627,7 @@ class CNode
627627

628628
public:
629629
uint256 hashContinue;
630-
int nStartingHeight;
630+
std::atomic<int> nStartingHeight;
631631

632632
// flood relay
633633
std::vector<CAddress> vAddrToSend;
@@ -665,15 +665,15 @@ class CNode
665665

666666
// Ping time measurement:
667667
// The pong reply we're expecting, or 0 if no pong expected.
668-
uint64_t nPingNonceSent;
668+
std::atomic<uint64_t> nPingNonceSent;
669669
// Time (in usec) the last ping was sent, or 0 if no ping was ever sent.
670-
int64_t nPingUsecStart;
670+
std::atomic<int64_t> nPingUsecStart;
671671
// Last measured round-trip time.
672-
int64_t nPingUsecTime;
672+
std::atomic<int64_t> nPingUsecTime;
673673
// Best measured round-trip time.
674-
int64_t nMinPingUsecTime;
674+
std::atomic<int64_t> nMinPingUsecTime;
675675
// Whether a ping is requested.
676-
bool fPingQueued;
676+
std::atomic<bool> fPingQueued;
677677
// Minimum fee rate with which to filter inv's to this node
678678
CAmount minFeeFilter;
679679
CCriticalSection cs_feeFilter;
@@ -694,6 +694,12 @@ class CNode
694694
const int nMyStartingHeight;
695695
int nSendVersion;
696696
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
697+
698+
mutable CCriticalSection cs_addrName;
699+
std::string addrName;
700+
701+
CService addrLocal;
702+
mutable CCriticalSection cs_addrLocal;
697703
public:
698704

699705
NodeId GetId() const {
@@ -727,6 +733,10 @@ class CNode
727733
void SetSendVersion(int nVersionIn);
728734
int GetSendVersion() const;
729735

736+
CService GetAddrLocal() const;
737+
//! May not be called more than once
738+
void SetAddrLocal(const CService& addrLocalIn);
739+
730740
CNode* AddRef()
731741
{
732742
nRefCount++;
@@ -796,6 +806,10 @@ class CNode
796806
{
797807
return nLocalServices;
798808
}
809+
810+
std::string GetAddrName() const;
811+
//! Sets the addrName only if it was not previously set
812+
void MaybeSetAddrName(const std::string& addrNameIn);
799813
};
800814

801815

src/net_processing.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# error "Bitcoin cannot be compiled without assertions."
3737
#endif
3838

39-
int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last received a block
39+
std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
4040

4141
struct IteratorComparator
4242
{
@@ -264,7 +264,7 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime)
264264

265265
void InitializeNode(CNode *pnode, CConnman& connman) {
266266
CAddress addr = pnode->addr;
267-
std::string addrName = pnode->addrName;
267+
std::string addrName = pnode->GetAddrName();
268268
NodeId nodeid = pnode->GetId();
269269
{
270270
LOCK(cs_main);
@@ -1211,6 +1211,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
12111211
int nVersion;
12121212
int nSendVersion;
12131213
std::string strSubVer;
1214+
std::string cleanSubVer;
12141215
int nStartingHeight = -1;
12151216
bool fRelay = true;
12161217

@@ -1246,6 +1247,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
12461247
vRecv >> addrFrom >> nNonce;
12471248
if (!vRecv.empty()) {
12481249
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
1250+
cleanSubVer = SanitizeString(strSubVer);
12491251
}
12501252
if (!vRecv.empty()) {
12511253
vRecv >> nStartingHeight;
@@ -1272,9 +1274,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
12721274
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
12731275

12741276
pfrom->nServices = nServices;
1275-
pfrom->addrLocal = addrMe;
1276-
pfrom->strSubVer = strSubVer;
1277-
pfrom->cleanSubVer = SanitizeString(strSubVer);
1277+
pfrom->SetAddrLocal(addrMe);
1278+
{
1279+
LOCK(pfrom->cs_SubVer);
1280+
pfrom->strSubVer = strSubVer;
1281+
pfrom->cleanSubVer = cleanSubVer;
1282+
}
12781283
pfrom->nStartingHeight = nStartingHeight;
12791284
pfrom->fClient = !(nServices & NODE_NETWORK);
12801285
{
@@ -1310,7 +1315,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
13101315
LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
13111316
pfrom->PushAddress(addr, insecure_rand);
13121317
} else if (IsPeerAddrLocalGood(pfrom)) {
1313-
addr.SetIP(pfrom->addrLocal);
1318+
addr.SetIP(addrMe);
13141319
LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
13151320
pfrom->PushAddress(addr, insecure_rand);
13161321
}
@@ -1330,7 +1335,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
13301335
remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
13311336

13321337
LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
1333-
pfrom->cleanSubVer, pfrom->nVersion,
1338+
cleanSubVer, pfrom->nVersion,
13341339
pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
13351340
remoteAddr);
13361341

@@ -2450,7 +2455,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24502455
if (pingUsecTime > 0) {
24512456
// Successful ping time measurement, replace previous
24522457
pfrom->nPingUsecTime = pingUsecTime;
2453-
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
2458+
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime);
24542459
} else {
24552460
// This should never happen
24562461
sProblem = "Timing mishap";

0 commit comments

Comments
 (0)