Skip to content
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

Merged
merged 9 commits into from
Jan 27, 2022

Conversation

jonatack
Copy link
Member

@jonatack jonatack commented Sep 9, 2021

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 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 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:

Mitigates/potentially closes #17161.

@DrahtBot
Copy link
Contributor

DrahtBot commented Sep 12, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #23927 (rpc: Pruning nodes can not fetch blocks before syncing past their height by fjahr)

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.

@jonatack
Copy link
Member Author

Rebased following the merge of #22895 that this was based on.

Copy link
Member

@maflcko maflcko left a 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)
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

@jonatack jonatack Sep 17, 2021

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.

@jonatack jonatack force-pushed the require-GetBlockPos-to-hold-cs_main branch from f6c2fdb to e950f0e Compare September 17, 2021 16:20
@jonatack
Copy link
Member Author

Thanks @MarcoFalke, updated with your suggestions per git diff f6c2fdb e950f0e

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();

@jonatack jonatack force-pushed the require-GetBlockPos-to-hold-cs_main branch from 285ecb2 to 0104df0 Compare September 17, 2021 18:10
@jonatack jonatack force-pushed the require-GetBlockPos-to-hold-cs_main branch from 0104df0 to a716569 Compare September 23, 2021 21:28
@jonatack
Copy link
Member Author

Rebased after the merge of #21526.

@jonatack jonatack force-pushed the require-GetBlockPos-to-hold-cs_main branch from a716569 to 9cacc50 Compare September 24, 2021 11:29
@jonatack jonatack changed the title consensus: require CBlockIndex::GetBlockPos() to hold mutex cs_main Guard CBlockIndex::nStatus by cs_main, require GetBlockPos/GetUndoPos to hold cs_main Oct 6, 2021
@jonatack jonatack force-pushed the require-GetBlockPos-to-hold-cs_main branch from cf2145b to 6ea5682 Compare January 25, 2022 20:06
@jonatack
Copy link
Member Author

Rebased.

This has concept ACKs by @MarcoFalke and @promag and ACKs by @hebasto, @achow101 and @vasild.

Review/re-ACKs welcome.

@jamesob
Copy link
Contributor

jamesob commented Jan 26, 2022

Benched 6ea5682; no performance impact 👍.

bench name command
ibd.local.range.500000.540000 bitcoind -dbcache=10000 -debug=coindb -debug=bench -listen=0 -connect=0 -addnode=127.0.0.1:8888 -prune=9999999 -printtoconsole=0 -assumevalid=000000000000000000176c192f42ad13ab159fdb20198b87e7ba3c001e47b876

#22932 vs. $mergebase (absolute)

bench name x #22932 $mergebase
ibd.local.range.500000.540000.total_secs 2 3093.6633 (± 17.1804) 3102.2931 (± 0.4422)
ibd.local.range.500000.540000.peak_rss_KiB 2 6355854.0000 (± 1646.0000) 6355764.0000 (± 1492.0000)
ibd.local.range.500000.540000.cpu_kernel_secs 2 263.2300 (± 3.3200) 264.0900 (± 0.1700)
ibd.local.range.500000.540000.cpu_user_secs 2 13020.2400 (± 4.7400) 13015.7250 (± 8.2850)

#22932 vs. $mergebase (relative)

bench name x #22932 $mergebase
ibd.local.range.500000.540000.total_secs 2 1.000 1.003
ibd.local.range.500000.540000.peak_rss_KiB 2 1.000 1.000
ibd.local.range.500000.540000.cpu_kernel_secs 2 1.000 1.003
ibd.local.range.500000.540000.cpu_user_secs 2 1.000 1.000

@laanwj
Copy link
Member

laanwj commented Jan 27, 2022

Code review ACK 6ea5682

@laanwj laanwj merged commit cf5bb04 into bitcoin:master Jan 27, 2022
@jonatack jonatack deleted the require-GetBlockPos-to-hold-cs_main branch January 27, 2022 10:11
Copy link
Contributor

@vasild vasild left a 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);
Copy link
Member

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.

Copy link
Member Author

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);
Copy link
Member

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?

Copy link
Member Author

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.

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 28, 2022
maflcko pushed a commit that referenced this pull request Jan 31, 2022
…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
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Feb 1, 2022
…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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
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
@bitcoin bitcoin locked and limited conversation to collaborators Jan 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CBlockIndex::nStatus race when pruning