-
Notifications
You must be signed in to change notification settings - Fork 36.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CBlockIndex lock annotations, guard nStatus/nFile/nDataPos/nUndoPos by cs_main #22932
Add CBlockIndex lock annotations, guard nStatus/nFile/nDataPos/nUndoPos by cs_main #22932
Conversation
c97282d
to
a2571de
Compare
a2571de
to
845de73
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
845de73
to
f6c2fdb
Compare
Rebased following the merge of #22895 that this was based on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK. Feel free to ignore the meta comment.
src/chain.h
Outdated
@@ -212,7 +215,8 @@ class CBlockIndex | |||
{ | |||
} | |||
|
|||
FlatFilePos GetBlockPos() const { | |||
FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is annotating the function, but not the underlying data member that needs the annotation.
Not sure how involved it will be to annotate everything properly here. Also, given that cs_main is the validation lock, we should (eventually/ideally) have a separate node_storage lock. Though, this again requires a major validation/node_storage rewrite.
However an argument against adding the lock annotation to the data member could be that chain
is supposed to be a data-structure-only primitive, like transaction
or block
. In that case only annotating the getter and setter functions (and making them standalone) would seem appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a quick look at annotating nStatus
to assess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appended a small commit to guard CBlockIndex::nStatus
by cs_main
as a starting point.
f6c2fdb
to
e950f0e
Compare
Thanks @MarcoFalke, updated with your suggestions per diff
diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp
index 311913b5db..1a663bd0b3 100644
--- a/src/test/util/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -11,8 +11,10 @@
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter)
{
+ LOCK(cs_main);
+
CBlock block;
- if (!ReadBlockFromDisk(block, WITH_LOCK(cs_main, return block_index->GetBlockPos()), Params().GetConsensus())) {
+ if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
return false;
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 37fd52a5e7..4ac8936ee7 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -86,8 +86,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
- const auto pos{WITH_LOCK(cs_main, return oldTip->GetBlockPos())};
- GetBlockFileInfo(pos.nFile)->nSize = MAX_BLOCKFILE_SIZE;
+ {
+ LOCK(cs_main);
+ GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
+ }
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip(); |
285ecb2
to
0104df0
Compare
0104df0
to
a716569
Compare
Rebased after the merge of #21526. |
a716569
to
9cacc50
Compare
Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
cf2145b
to
6ea5682
Compare
Rebased. This has concept ACKs by @MarcoFalke and @promag and ACKs by @hebasto, @achow101 and @vasild. Review/re-ACKs welcome. |
Benched 6ea5682; no performance impact 👍.
#22932 vs. $mergebase (absolute)
#22932 vs. $mergebase (relative)
|
Code review ACK 6ea5682 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 6ea5682
@@ -382,6 +382,7 @@ class CDiskBlockIndex : public CBlockIndex | |||
|
|||
SERIALIZE_METHODS(CDiskBlockIndex, obj) | |||
{ | |||
LOCK(::cs_main); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain why this is needed?
CDiskBlockIndex
creates a copy of the data and it isn't used in validation or blockstorage code. It is only used to serialize a struct and then discarded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain why this is needed?
CDiskBlockIndex
creates a copy of the data and it isn't used in validation or blockstorage code. It is only used to serialize a struct and then discarded.
Good point. Opened #24199 as an approach to address this.
pindexNew->nTx = diskindex.nTx; | ||
{ | ||
LOCK(::cs_main); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why this is needed? The calling function should already hold cs_main?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch--we can drop the lock in favor of an annotation; done in #24197.
…eDB::LoadBlockIndexGuts() 20276ca Replace lock with thread safety annotation in CBlockTreeDB::LoadBlockIndexGuts() (Jon Atack) Pull request description: Following up on #22932 (comment) by Marco Falke (good observation, thank you), we can replace a cs_main lock in `CBlockTreeDB::LoadBlockIndexGuts()` with a Clang thread safety annotation/assertion instead. The unlocked code is reverted to its original state before #22932. ACKs for top commit: hebasto: ACK 20276ca, I have reviewed the code and it looks OK, I agree it can be merged. Tree-SHA512: 2d91d1c962af0286d835d92a56396a27ea00e9d061b869eaff9421b448a083b0513828e1d4df7504396896057bf1e344e180a50271a5cfd1e1c7b6527155b2bb
…lockTreeDB::LoadBlockIndexGuts() 20276ca Replace lock with thread safety annotation in CBlockTreeDB::LoadBlockIndexGuts() (Jon Atack) Pull request description: Following up on bitcoin#22932 (comment) by Marco Falke (good observation, thank you), we can replace a cs_main lock in `CBlockTreeDB::LoadBlockIndexGuts()` with a Clang thread safety annotation/assertion instead. The unlocked code is reverted to its original state before bitcoin#22932. ACKs for top commit: hebasto: ACK 20276ca, I have reviewed the code and it looks OK, I agree it can be merged. Tree-SHA512: 2d91d1c962af0286d835d92a56396a27ea00e9d061b869eaff9421b448a083b0513828e1d4df7504396896057bf1e344e180a50271a5cfd1e1c7b6527155b2bb
Summary: > consensus: don't call GetBlockPos in ReadBlockFromDisk without lock The `cs_main` lock only covered the first invocation of `CBlockIndex::GetBlockPos()`. Use the `blockPos` local variable instead of calling the function twice, rename it to `block_pos`, and make it const This is a backport of [[bitcoin/bitcoin#22895 | core#22895]] ---- > Require CBlockIndex::GetBlockPos() to hold mutex cs_main This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@6fd4341 Test Plan: With clang and DEBUG: `ninja all check-all bitcoin-fuzzers` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13031
Summary: Mutex cs_main is already held by the caller of WriteUndoDataForBlock(). This change is needed to require CBlockIndex::GetUndoPos() to hold cs_main and CBlockIndex::nStatus to be guarded by cs_main in the following commits without adding 2 unnecessary cs_main locks to WriteUndoDataForBlock(). This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@2e557ce Depends on D13031 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13032
Summary: This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@5723934 Depends on D13032 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13033
Summary: This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@e9f3aa5 Depends on D13033 Test Plan: With clang and DEBUG: `ninja all check-all bitcoin-fuzzers` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13034
Summary: This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@8ef457c Depends on D13034 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13035
Summary: This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@ca47b00 Depends on D13035 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D13036
Summary: Co-authored-by: Vasil Dimov <vd@FreeBSD.org> This is a partial backport of [[bitcoin/bitcoin#22932 | core#22932]] bitcoin/bitcoin@eaeeb88 Depends on D13036 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D13037
Summary: > CBlockIndex::nStatus may be racy, i.e. potentially accessed by multiple threads, see [[bitcoin/bitcoin#17161 | core#17161]]. A solution is to guard it by cs_main, along with fellow data members nFile, nDataPos and nUndoPos. Co-authored-by: Vasil Dimov <vd@FreeBSD.org> This concludes backport of [[bitcoin/bitcoin#22932 | core#22932]] and [[bitcoin/bitcoin#24197 | core#24197]] bitcoin/bitcoin@6ea5682 bitcoin/bitcoin@20276ca Depends on D13037 Test Plan: With clang and DEBUG: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D13038
Issues:
CBlockIndex
member functionsGetBlockPos()
,GetUndoPos()
,IsAssumedValid()
,RaiseValidity()
, andIsValid()
and block storage functionsWriteUndoDataForBlock()
andIsBlockPruned()
are missing thread safety lock annotations to help ensure that they are called with mutex cs_main to avoid bugs like consensus: don't call GetBlockPos in ReadBlockFromDisk without cs_main lock #22895. Doing this also enables the next step:CBlockIndex::nStatus
may be racy, i.e. potentially accessed by multiple threads, see CBlockIndex::nStatus race when pruning #17161. A solution is to guard it by cs_main, along with fellow data membersnFile
,nDataPos
andnUndoPos
.This pull:
CBlockIndex::nStatus
,nFile
,nDataPos
andnUndoPos
by cs_mainHow to review and test:
-Wthread-safety-analysis
warningsMitigates/potentially closes #17161.