Skip to content

Commit cf5bb04

Browse files
committed
Merge bitcoin/bitcoin#22932: Add CBlockIndex lock annotations, guard nStatus/nFile/nDataPos/nUndoPos by cs_main
6ea5682 Guard CBlockIndex::nStatus/nFile/nDataPos/nUndoPos by cs_main (Jon Atack) 5d59ae0 Remove/inline ReadRawBlockFromDisk(block_data, pindex, message_start) (Hennadii Stepanov) eaeeb88 Require IsBlockPruned() to hold mutex cs_main (Jon Atack) ca47b00 Require CBlockIndex::IsValid() to hold cs_main (Vasil Dimov) e9f3aa5 Require CBlockIndex::RaiseValidity() to hold cs_main (Vasil Dimov) 8ef457c Require CBlockIndex::IsAssumedValid() to hold cs_main (Vasil Dimov) 5723934 Require CBlockIndex::GetUndoPos() to hold mutex cs_main (Jon Atack) 2e557ce Require WriteUndoDataForBlock() to hold mutex cs_main (Jon Atack) 6fd4341 Require CBlockIndex::GetBlockPos() to hold mutex cs_main (Jon Atack) Pull request description: Issues: - `CBlockIndex` member functions `GetBlockPos()`, `GetUndoPos()`, `IsAssumedValid()`, `RaiseValidity()`, and `IsValid()` and block storage functions `WriteUndoDataForBlock()` and `IsBlockPruned()` are missing thread safety lock annotations to help ensure that they are called with mutex cs_main to avoid bugs like #22895. Doing this also enables the next step: - `CBlockIndex::nStatus` may be racy, i.e. potentially accessed by multiple threads, see #17161. A solution is to guard it by cs_main, along with fellow data members `nFile`, `nDataPos` and `nUndoPos`. This pull: - adds thread safety lock annotations for the functions listed above - guards `CBlockIndex::nStatus`, `nFile`, `nDataPos` and `nUndoPos` by cs_main How to review and test: - debug build with clang and verify there are no `-Wthread-safety-analysis` warnings - review the code to verify each annotation or lock is necessary and sensible, or if any are missing - look for whether taking a lock can be replaced by a lock annotation instead - for more information about Clang thread safety analysis, see - https://clang.llvm.org/docs/ThreadSafetyAnalysis.html - https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#lockingmutex-usage-notes - https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#threads-and-synchronization Mitigates/potentially closes #17161. ACKs for top commit: laanwj: Code review ACK 6ea5682 Tree-SHA512: 3ebf429c8623c51f944a7245a2e48d2aa088dec4c4914b40aa6049e89856c1ee8586f6e2e3b65195190566637a33004468b51a781e61a082248748015167569b
2 parents d87a37a + 6ea5682 commit cf5bb04

13 files changed

+82
-52
lines changed

src/chain.h

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <consensus/params.h>
1111
#include <flatfile.h>
1212
#include <primitives/block.h>
13+
#include <sync.h>
1314
#include <tinyformat.h>
1415
#include <uint256.h>
1516

@@ -37,6 +38,8 @@ static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
3738
*/
3839
static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60;
3940

41+
extern RecursiveMutex cs_main;
42+
4043
class CBlockFileInfo
4144
{
4245
public:
@@ -161,13 +164,13 @@ class CBlockIndex
161164
int nHeight{0};
162165

163166
//! Which # file this block is stored in (blk?????.dat)
164-
int nFile{0};
167+
int nFile GUARDED_BY(::cs_main){0};
165168

166169
//! Byte offset within blk?????.dat where this block's data is stored
167-
unsigned int nDataPos{0};
170+
unsigned int nDataPos GUARDED_BY(::cs_main){0};
168171

169172
//! Byte offset within rev?????.dat where this block's undo data is stored
170-
unsigned int nUndoPos{0};
173+
unsigned int nUndoPos GUARDED_BY(::cs_main){0};
171174

172175
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
173176
arith_uint256 nChainWork{};
@@ -195,7 +198,7 @@ class CBlockIndex
195198
//! load to avoid the block index being spuriously rewound.
196199
//! @sa NeedsRedownload
197200
//! @sa ActivateSnapshot
198-
uint32_t nStatus{0};
201+
uint32_t nStatus GUARDED_BY(::cs_main){0};
199202

200203
//! block header
201204
int32_t nVersion{0};
@@ -223,8 +226,9 @@ class CBlockIndex
223226
{
224227
}
225228

226-
FlatFilePos GetBlockPos() const
229+
FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
227230
{
231+
AssertLockHeld(::cs_main);
228232
FlatFilePos ret;
229233
if (nStatus & BLOCK_HAVE_DATA) {
230234
ret.nFile = nFile;
@@ -233,8 +237,9 @@ class CBlockIndex
233237
return ret;
234238
}
235239

236-
FlatFilePos GetUndoPos() const
240+
FlatFilePos GetUndoPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
237241
{
242+
AssertLockHeld(::cs_main);
238243
FlatFilePos ret;
239244
if (nStatus & BLOCK_HAVE_UNDO) {
240245
ret.nFile = nFile;
@@ -306,7 +311,9 @@ class CBlockIndex
306311

307312
//! Check whether this block index entry is valid up to the passed validity level.
308313
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
314+
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
309315
{
316+
AssertLockHeld(::cs_main);
310317
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
311318
if (nStatus & BLOCK_FAILED_MASK)
312319
return false;
@@ -315,12 +322,17 @@ class CBlockIndex
315322

316323
//! @returns true if the block is assumed-valid; this means it is queued to be
317324
//! validated by a background chainstate.
318-
bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
325+
bool IsAssumedValid() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
326+
{
327+
AssertLockHeld(::cs_main);
328+
return nStatus & BLOCK_ASSUMED_VALID;
329+
}
319330

320331
//! Raise the validity level of this block index entry.
321332
//! Returns true if the validity was changed.
322-
bool RaiseValidity(enum BlockStatus nUpTo)
333+
bool RaiseValidity(enum BlockStatus nUpTo) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
323334
{
335+
AssertLockHeld(::cs_main);
324336
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
325337
if (nStatus & BLOCK_FAILED_MASK) return false;
326338

@@ -370,6 +382,7 @@ class CDiskBlockIndex : public CBlockIndex
370382

371383
SERIALIZE_METHODS(CDiskBlockIndex, obj)
372384
{
385+
LOCK(::cs_main);
373386
int _nVersion = s.GetVersion();
374387
if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT_MODE(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
375388

src/index/txindex.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
5959
// Exclude genesis block transaction because outputs are not spendable.
6060
if (pindex->nHeight == 0) return true;
6161

62-
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
62+
CDiskTxPos pos{
63+
WITH_LOCK(::cs_main, return pindex->GetBlockPos()),
64+
GetSizeOfCompactSize(block.vtx.size())};
6365
std::vector<std::pair<uint256, CDiskTxPos>> vPos;
6466
vPos.reserve(block.vtx.size());
6567
for (const auto& tx : block.vtx) {

src/net_processing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1880,7 +1880,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
18801880
// Fast-path: in this case it is possible to serve the block directly from disk,
18811881
// as the network format matches the format on disk
18821882
std::vector<uint8_t> block_data;
1883-
if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) {
1883+
if (!ReadRawBlockFromDisk(block_data, pindex->GetBlockPos(), m_chainparams.MessageStart())) {
18841884
assert(!"cannot load block from disk");
18851885
}
18861886
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data}));

src/node/blockstorage.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
429429

430430
bool IsBlockPruned(const CBlockIndex* pblockindex)
431431
{
432+
AssertLockHeld(::cs_main);
432433
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
433434
}
434435

@@ -513,7 +514,8 @@ static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const
513514

514515
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
515516
{
516-
FlatFilePos pos = pindex->GetUndoPos();
517+
const FlatFilePos pos{WITH_LOCK(::cs_main, return pindex->GetUndoPos())};
518+
517519
if (pos.IsNull()) {
518520
return error("%s: no undo data available", __func__);
519521
}
@@ -712,6 +714,7 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa
712714

713715
bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
714716
{
717+
AssertLockHeld(::cs_main);
715718
// Write undo information to disk
716719
if (pindex->GetUndoPos().IsNull()) {
717720
FlatFilePos _pos;
@@ -818,17 +821,6 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
818821
return true;
819822
}
820823

821-
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
822-
{
823-
FlatFilePos block_pos;
824-
{
825-
LOCK(cs_main);
826-
block_pos = pindex->GetBlockPos();
827-
}
828-
829-
return ReadRawBlockFromDisk(block, block_pos, message_start);
830-
}
831-
832824
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
833825
FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp)
834826
{

src/node/blockstorage.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77

88
#include <fs.h>
99
#include <protocol.h> // For CMessageHeader::MessageStartChars
10+
#include <sync.h>
1011
#include <txdb.h>
1112

1213
#include <atomic>
1314
#include <cstdint>
1415
#include <vector>
1516

17+
extern RecursiveMutex cs_main;
18+
1619
class ArgsManager;
1720
class BlockValidationState;
1821
class CBlock;
@@ -146,7 +149,8 @@ class BlockManager
146149
/** Get block file info entry for one block file */
147150
CBlockFileInfo* GetBlockFileInfo(size_t n);
148151

149-
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams);
152+
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
153+
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
150154

151155
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);
152156

@@ -163,7 +167,7 @@ class BlockManager
163167
};
164168

165169
//! Check whether the block associated with this index entry is pruned or not.
166-
bool IsBlockPruned(const CBlockIndex* pblockindex);
170+
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
167171

168172
void CleanupBlockRevFiles();
169173

@@ -181,7 +185,6 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
181185
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
182186
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
183187
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
184-
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
185188

186189
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
187190

src/rpc/blockchain.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
185185
case TxVerbosity::SHOW_DETAILS:
186186
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
187187
CBlockUndo blockUndo;
188-
const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
188+
const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
189189

190190
for (size_t i = 0; i < block.vtx.size(); ++i) {
191191
const CTransactionRef& tx = block.vtx.at(i);
@@ -818,7 +818,8 @@ static RPCHelpMan getblockfrompeer()
818818
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
819819
}
820820

821-
if (index->nStatus & BLOCK_HAVE_DATA) {
821+
const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
822+
if (block_has_data) {
822823
throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
823824
}
824825

@@ -929,8 +930,9 @@ static RPCHelpMan getblockheader()
929930
};
930931
}
931932

932-
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
933+
static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
933934
{
935+
AssertLockHeld(::cs_main);
934936
CBlock block;
935937
if (IsBlockPruned(pblockindex)) {
936938
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
@@ -946,8 +948,9 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
946948
return block;
947949
}
948950

949-
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
951+
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
950952
{
953+
AssertLockHeld(::cs_main);
951954
CBlockUndo blockUndo;
952955
if (IsBlockPruned(pblockindex)) {
953956
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");

src/rpc/rawtransaction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ static RPCHelpMan getrawtransaction()
241241
if (!tx) {
242242
std::string errmsg;
243243
if (blockindex) {
244-
if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
244+
const bool block_has_data = WITH_LOCK(::cs_main, return blockindex->nStatus & BLOCK_HAVE_DATA);
245+
if (!block_has_data) {
245246
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
246247
}
247248
errmsg = "No such transaction found in the provided block";

src/test/fuzz/chain.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ FUZZ_TARGET(chain)
2121

2222
const uint256 zero{};
2323
disk_block_index->phashBlock = &zero;
24-
(void)disk_block_index->GetBlockHash();
25-
(void)disk_block_index->GetBlockPos();
26-
(void)disk_block_index->GetBlockTime();
27-
(void)disk_block_index->GetBlockTimeMax();
28-
(void)disk_block_index->GetMedianTimePast();
29-
(void)disk_block_index->GetUndoPos();
30-
(void)disk_block_index->HaveTxsDownloaded();
31-
(void)disk_block_index->IsValid();
32-
(void)disk_block_index->ToString();
24+
{
25+
LOCK(::cs_main);
26+
(void)disk_block_index->GetBlockHash();
27+
(void)disk_block_index->GetBlockPos();
28+
(void)disk_block_index->GetBlockTime();
29+
(void)disk_block_index->GetBlockTimeMax();
30+
(void)disk_block_index->GetMedianTimePast();
31+
(void)disk_block_index->GetUndoPos();
32+
(void)disk_block_index->HaveTxsDownloaded();
33+
(void)disk_block_index->IsValid();
34+
(void)disk_block_index->ToString();
35+
}
3336

3437
const CBlockHeader block_header = disk_block_index->GetBlockHeader();
3538
(void)CDiskBlockIndex{*disk_block_index};
@@ -55,7 +58,7 @@ FUZZ_TARGET(chain)
5558
if (block_status & ~BLOCK_VALID_MASK) {
5659
continue;
5760
}
58-
(void)disk_block_index->RaiseValidity(block_status);
61+
WITH_LOCK(::cs_main, (void)disk_block_index->RaiseValidity(block_status));
5962
}
6063

6164
CBlockIndex block_index{block_header};

src/test/interfaces_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
123123

124124
BOOST_AUTO_TEST_CASE(hasBlocks)
125125
{
126+
LOCK(::cs_main);
126127
auto& chain = m_node.chain;
127128
const CChain& active = Assert(m_node.chainman)->ActiveChain();
128129

src/test/util/blockfilter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ using node::UndoReadFromDisk;
1313

1414
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter)
1515
{
16+
LOCK(::cs_main);
17+
1618
CBlock block;
1719
if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
1820
return false;

0 commit comments

Comments
 (0)