Skip to content

Conversation

dongcarl
Copy link
Contributor

@dongcarl dongcarl commented Jan 28, 2021

This avoids a potential race-condition where a thread is reading the
ChainstateManager::m_active_chainstate pointer while another one is
writing to it. There is no portable guarantee that reading/writing the
pointer is thread-safe.

This is also done in way that mimics ::ChainstateActive(), so the
transition from that function to this method is easy.

More discussion:
1. https://github.com/bitcoin/bitcoin/pull/20749#discussion_r559544027
2. https://github.com/bitcoin/bitcoin/pull/19806#discussion_r561023961
3. https://github.com/bitcoin/bitcoin/pull/19806#issuecomment-768946522
4. https://github.com/bitcoin/bitcoin/pull/19806#issuecomment-768955695

Basically this PR removes the loaded-but-unfired footgun, which:

@dongcarl dongcarl force-pushed the 2021-01-chainman-activechainstate-locking branch from c1d4af7 to 5b8118e Compare January 28, 2021 18:50
@dongcarl dongcarl force-pushed the 2021-01-chainman-activechainstate-locking branch from 5b8118e to f92dc65 Compare January 28, 2021 19:14
Copy link
Contributor

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK f92dc65

I've left a few style comments, but this seems fine.

@dongcarl dongcarl force-pushed the 2021-01-chainman-activechainstate-locking branch from f92dc65 to e6eb8d2 Compare January 28, 2021 20:48
@laanwj
Copy link
Member

laanwj commented Jan 28, 2021

Code review ACK e6eb8d2

@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 29, 2021

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

Conflicts

No conflicts as of last run.

@practicalswift
Copy link
Contributor

cr ACK e6eb8d2: patch looks correct

@maflcko
Copy link
Member

maflcko commented Jan 29, 2021

Any reason to only add the annotation to (1/3) of the m_*_chainstate? The others have the same requirement

@jnewbery
Copy link
Contributor

jnewbery commented Feb 1, 2021

Code review ACK e6eb8d2

Any reason to only add the annotation to (1/3) of the m_*_chainstate? The others have the same requirement

This resolves the blocker for #20749, but I agree that the other m_*_chainstates should be guarded by cs_main.

@jnewbery
Copy link
Contributor

jnewbery commented Feb 1, 2021

The older version was merged as part of #20749. Perhaps we should repurpose this PR to address the review comments (particularly #21025 (comment))?

@dongcarl dongcarl force-pushed the 2021-01-chainman-activechainstate-locking branch 3 times, most recently from 3f24a72 to 24fcc4c Compare February 1, 2021 16:47
@dongcarl dongcarl changed the title validation: Guard the active_chainstate with cs_main validation: Guard chainman chainstates with cs_main Feb 1, 2021
@dongcarl
Copy link
Contributor Author

dongcarl commented Feb 1, 2021

As suggested by @jnewbery, this PR has been repurposed to address #21025 (comment)

Since these chainstates are:

1. Also vulnerable to the race condition described in the previous
   commit
2. Documented as having similar semantics as m_active_chainstate

we should also protect them with ::cs_main.
@dongcarl dongcarl force-pushed the 2021-01-chainman-activechainstate-locking branch from 24fcc4c to 20677ff Compare February 2, 2021 03:09
@dongcarl
Copy link
Contributor Author

dongcarl commented Feb 2, 2021

Pushed 24fcc4c -> 20677ff

@jnewbery
Copy link
Contributor

jnewbery commented Feb 2, 2021

code review ACK 20677ff. I've verified by eye that neither of these members are accessed without cs_main.

In the commit message, "Also vulnerable to the race condition described in the previous commit" no longer makes sense since f92dc65 is not the previous commit.

A couple of potential follow-ups:

  • it may be possible to initialize these fields in the initializer list and mark them const. That'd be even better than guarding them with cs_main, since it wouldn't need to be locked every time they're accessed.
  • perhaps InitializeChainstate() and MaybeRebalanceCaches() should have AssertLockHeld(cs_main) added. Those are the only two functions where these members are accessed and it's not immediately obvious that cs_main is already being held.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 20677ff. It is safer to have these new GUARDED_BY annotations and locks than not to have them, but in the longer run I think every LOCK(cs_main) added here and added earlier in f92dc65 from #20749 should be removed and replaced with EXCLUSIVE_LOCKS_REQUIRED(cs_main) on the accessor methods instead. cs_main is a high level lock that should be explicitly acquired at a high level to prevent the chain state from changing. It shouldn't be acquired recursively in low-level methods just to read pointer values atomically.

After #19806 and its followups, the active chainstate pointer will be able to change at runtime, and code calling these accessor methods should care if the results they return are still up-to-date after returning. That code should be explicitly locking cs_main and making sure state is locked the right amount of time, not relying on recursive locking and potentially using outdated or inconsistent state pointers.

IIUC changing this now would make #19806 more difficult to rebase so is reasonable to delay. But after #19806 is done with, it would be good for all the new locks added in f92dc65 and
20677ff to be removed and replaced with lock required annotations, and for calling code like ProcessNewBlock to either lock or save pointers correctly and not rely on ActiveChainstate() and related methods return values to not change.

@maflcko maflcko merged commit f1239b7 into bitcoin:master Feb 4, 2021
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Feb 4, 2021
20677ff validation: Guard all chainstates with cs_main (Carl Dong)

Pull request description:

  ```
  This avoids a potential race-condition where a thread is reading the
  ChainstateManager::m_active_chainstate pointer while another one is
  writing to it. There is no portable guarantee that reading/writing the
  pointer is thread-safe.

  This is also done in way that mimics ::ChainstateActive(), so the
  transition from that function to this method is easy.

  More discussion:
  1. bitcoin#20749 (comment)
  2. bitcoin#19806 (comment)
  3. bitcoin#19806 (comment)
  4. bitcoin#19806 (comment)
  ```

  Basically this PR removes the loaded-but-unfired footgun, which:
  - Is multiplied (but still unshot) in the chainman deglobalization PRs (bitcoin#20158)
  - Is shot in the test framework in the au.activate PR (bitcoin#19806)

ACKs for top commit:
  jnewbery:
    code review ACK 20677ff. I've verified by eye that neither of these members are accessed without cs_main.
  ryanofsky:
    Code review ACK 20677ff. It is safer to have these new `GUARDED_BY` annotations and locks than not to have them, but in the longer run I think every `LOCK(cs_main)` added here and added earlier in f92dc65 from bitcoin#20749 should be removed and replaced with `EXCLUSIVE_LOCKS_REQUIRED(cs_main)` on the accessor methods instead. `cs_main` is a high level lock that should be explicitly acquired at a high level to prevent the chain state from changing. It shouldn't be acquired recursively in low-level methods just to read pointer values atomically.

Tree-SHA512: 68a3a46d79a407b774eab77e1d682a97e95f1672db0a5fcb877572e188bec09f3a7b47c5d0cc1f2769ea276896dcbe97cb35c861acf7d8e3e513e955dc773f89
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jul 21, 2022
Summary:
PR description:
> This avoids a potential race-condition where a thread is reading the
> ChainstateManager::m_active_chainstate pointer while another one is
> writing to it. There is no portable guarantee that reading/writing the
> pointer is thread-safe.

This is a backport of [[bitcoin/bitcoin#21025 | core#21025]]

Test Plan:
With debug and tsan:
`ninja check check-functional`

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Differential Revision: https://reviews.bitcoinabc.org/D11773
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
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.

7 participants