Skip to content

Commit c9017ce

Browse files
jamesobryanofsky
andcommitted
protect g_chainman with cs_main
I'd previously attempted to create a specialized lock for ChainstateManager, but it turns out that because that lock would be required for functions like ChainActive() and ChainstateActive(), it created irreconcilable lock inversions since those functions are used so broadly throughout the codebase. Instead, I'm just using cs_main to protect the contents of g_chainman. Co-authored-by: Russell Yanofsky <[email protected]>
1 parent 2b081c4 commit c9017ce

File tree

5 files changed

+48
-11
lines changed

5 files changed

+48
-11
lines changed

src/init.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,17 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
709709
}
710710

711711
// scan for better chains in the block chain database, that are not yet connected in the active best chain
712-
BlockValidationState state;
713-
if (!ActivateBestChain(state, chainparams)) {
714-
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
715-
StartShutdown();
716-
return;
712+
713+
// We can't hold cs_main during ActivateBestChain even though we're accessing
714+
// the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
715+
// the relevant pointers before the ABC call.
716+
for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
717+
BlockValidationState state;
718+
if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
719+
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
720+
StartShutdown();
721+
return;
722+
}
717723
}
718724

719725
if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
@@ -1622,8 +1628,9 @@ bool AppInitMain(NodeContext& node)
16221628
}
16231629

16241630
bool failed_rewind{false};
1625-
1626-
for (CChainState* chainstate : g_chainman.GetAll()) {
1631+
// Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
1632+
// chainstates beforehand.
1633+
for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
16271634
if (!fReset) {
16281635
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
16291636
// It both disconnects blocks based on the chainstate, and drops block data in

src/qt/test/apptests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void AppTests::appTests()
8282
// Reset global state to avoid interfering with later tests.
8383
AbortShutdown();
8484
UnloadBlockIndex();
85-
g_chainman.Reset();
85+
WITH_LOCK(::cs_main, g_chainman.Reset());
8686
}
8787

8888
//! Entry point for BitcoinGUI tests.

src/validation.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ ChainstateManager g_chainman;
8181

8282
CChainState& ChainstateActive()
8383
{
84+
LOCK(::cs_main);
8485
assert(g_chainman.m_active_chainstate);
8586
return *g_chainman.m_active_chainstate;
8687
}
8788

8889
CChain& ChainActive()
8990
{
91+
LOCK(::cs_main);
9092
return ::ChainstateActive().m_chain;
9193
}
9294

@@ -1295,6 +1297,7 @@ static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
12951297

12961298
BlockMap& BlockIndex()
12971299
{
1300+
LOCK(::cs_main);
12981301
return g_chainman.m_blockman.m_block_index;
12991302
}
13001303

@@ -4704,7 +4707,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
47044707
// Activate the genesis block so normal node progress can continue
47054708
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
47064709
BlockValidationState state;
4707-
if (!ActivateBestChain(state, chainparams)) {
4710+
if (!ActivateBestChain(state, chainparams, nullptr)) {
47084711
break;
47094712
}
47104713
}

src/validation.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,14 +823,41 @@ class ChainstateManager
823823
//! free the chainstate contents immediately after it finishes validation
824824
//! to cautiously avoid a case where some other part of the system is still
825825
//! using this pointer (e.g. net_processing).
826+
//!
827+
//! Once this pointer is set to a corresponding chainstate, it will not
828+
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
829+
//! the contents of this pointer with ::cs_main held, release the lock,
830+
//! and then use the reference without concern of it being deconstructed.
831+
//!
832+
//! This is especially important when, e.g., calling ActivateBestChain()
833+
//! on all chainstates because we are not able to hold ::cs_main going into
834+
//! that call.
826835
std::unique_ptr<CChainState> m_ibd_chainstate;
827836

828837
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
829838
//! non-null, it is always our active chainstate.
839+
//!
840+
//! Once this pointer is set to a corresponding chainstate, it will not
841+
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
842+
//! the contents of this pointer with ::cs_main held, release the lock,
843+
//! and then use the reference without concern of it being deconstructed.
844+
//!
845+
//! This is especially important when, e.g., calling ActivateBestChain()
846+
//! on all chainstates because we are not able to hold ::cs_main going into
847+
//! that call.
830848
std::unique_ptr<CChainState> m_snapshot_chainstate;
831849

832850
//! Points to either the ibd or snapshot chainstate; indicates our
833851
//! most-work chain.
852+
//!
853+
//! Once this pointer is set to a corresponding chainstate, it will not
854+
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
855+
//! the contents of this pointer with ::cs_main held, release the lock,
856+
//! and then use the reference without concern of it being deconstructed.
857+
//!
858+
//! This is especially important when, e.g., calling ActivateBestChain()
859+
//! on all chainstates because we are not able to hold ::cs_main going into
860+
//! that call.
834861
CChainState* m_active_chainstate{nullptr};
835862

836863
//! If true, the assumed-valid chainstate has been fully validated
@@ -894,7 +921,7 @@ class ChainstateManager
894921
void Reset();
895922
};
896923

897-
extern ChainstateManager g_chainman;
924+
extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
898925

899926
/** @returns the most-work valid chainstate. */
900927
CChainState& ChainstateActive();

src/wallet/test/wallet_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ class ListCoinsTestingSetup : public TestChain100Setup
454454
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
455455
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
456456
{
457-
LOCK(wallet->cs_wallet);
457+
LOCK2(::cs_main, wallet->cs_wallet);
458458
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
459459
}
460460
bool firstRun;

0 commit comments

Comments
 (0)