wallet: Add missing cs_wallet/cs_KeyStore locks to wallet #11634

pull practicalswift wants to merge 4 commits into bitcoin:master from practicalswift:missing-wallet-locks changing 5 files +39 −28
  1. practicalswift commented at 9:33 AM on November 8, 2017: contributor

    Add missing wallet locks:

    • Calling the function GetConflicts(...) requires holding the mutex cs_wallet
    • Calling the function IsSpent(...) requires holding the mutex cs_wallet
    • Accessing the variables mapKeys and mapCryptedKeys requires holding the mutex cs_KeyStore
    • Accessing the variable nTimeFirstKey requires holding the mutex cs_wallet
    • Accessing the variable mapWallet requires holding the mutex cs_wallet
    • Accessing the variable nTimeFirstKey requires holding the mutex cs_wallet
  2. fanquake added the label Wallet on Nov 8, 2017
  3. practicalswift force-pushed on Nov 8, 2017
  4. in src/wallet/wallet.cpp:4089 in b229a8ea1d outdated
    3959 | @@ -3953,6 +3960,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
    3960 |              for (const CWalletTx& wtxOld : vWtx)
    3961 |              {
    3962 |                  uint256 hash = wtxOld.GetHash();
    3963 | +                LOCK(walletInstance->cs_wallet);
    


    promag commented at 1:39 PM on November 8, 2017:

    Either:

    • lock once before the loop;
    • loop first with the lock to mapWallet.find(hash) for all vWtx and then loop again without the lock to walletdb.WriteTx();
    • lock only the mapWallet.find(hash).
  5. practicalswift force-pushed on Nov 8, 2017
  6. practicalswift commented at 1:53 PM on November 8, 2017: contributor

    @promag Thanks for reviewing! Feedback addressed. Looks good? :-)

  7. in src/wallet/wallet.cpp:4305 in e921615a90 outdated
    3956 | @@ -3950,6 +3957,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
    3957 |          {
    3958 |              CWalletDB walletdb(*walletInstance->dbw);
    3959 |  
    3960 | +            LOCK(walletInstance->cs_wallet);
    


    promag commented at 3:08 PM on November 8, 2017:

    This should be before the line above. Anyway, there is no need to lock this wallet since it's a fresh instance, unknown to the remaining system. Unless you are adding the lock because of other asserts and if so please commit them to justify this change?


    practicalswift commented at 4:08 PM on November 8, 2017:

    Now moved before the line above. Without that lock we'll trigger Clang thread safety analysis warnings when #11634 is merged due to:

    src/wallet/wallet.h:    std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
    

    As agreed upon in #11226 (comment) all guard/lock annotations are added in #11634 and all extra locking is submitted as separate PR:s (such as this one).

    The annotations are compile-time only whereas the locks change run-time behaviour (and thus needs extra scrunity!).


    promag commented at 4:18 PM on November 8, 2017:

    As agreed upon in #11226 (comment) all guard/lock annotations are added in #11634 and all extra locking is submitted as separate PR:s (such as this one)

    I think you swapped the PR numbers?


    practicalswift commented at 9:55 PM on November 8, 2017:

    Yes, you're right - the annotations are added in #11226 :-)

  8. practicalswift force-pushed on Nov 8, 2017
  9. promag commented at 8:36 PM on November 8, 2017: member

    utACK 007fcbf.

  10. ryanofsky cross-referenced this on Nov 10, 2017 from issue [WIP] Add Clang thread safety analysis annotations: GUARDED_BY(lock) / EXCLUSIVE_LOCKS_REQUIRED(lock) by practicalswift
  11. luke-jr referenced this in commit a6b3c637e6 on Nov 11, 2017
  12. practicalswift force-pushed on Nov 21, 2017
  13. practicalswift commented at 4:58 PM on November 21, 2017: contributor

    Added another commit with two more missing locks:

    • calling function IsSpent requires holding mutex pwallet->cs_wallet exclusively
    • writing variable nWalletVersion, nWalletMaxVersion, nOrderPosNext and nTimeFirstKey require holding mutex cs_wallet
  14. practicalswift commented at 4:59 PM on November 21, 2017: contributor

    @promag Would you mind re-reviewing? :-)

  15. in src/wallet/wallet.cpp:4283 in fba0830bca outdated
    3995 | @@ -3991,8 +3996,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
    3996 |  
    3997 |          // No need to read and scan block if block was created before
    3998 |          // our wallet birthday (as adjusted for block time variability)
    3999 | -        while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
    4000 | -            pindexRescan = chainActive.Next(pindexRescan);
    4001 | +        {
    4002 | +            LOCK(walletInstance->cs_wallet);
    


    TheBlueMatt commented at 10:38 PM on December 12, 2017:

    Given this is effectively just the wallet's constructor, might as well just take the lock further up and hold it the whole time.


    practicalswift commented at 3:31 PM on March 12, 2018:

    @TheBlueMatt Moving it further up will make the scope of the walletInstance->cs_wallet lock cover the ScanForWalletTransactions call. ScanForWalletTransactions locks cs_main giving us the lock order:

      1. cs_wallet
      1. cs_main

    This is a potential deadlock given that the opposite lock order is used extensively:

    $ git grep "LOCK2(cs_main,.*cs_wallet);" | wc -l
    89
    

    Please advice :-)


    promag commented at 9:23 PM on March 14, 2018:

    A solution to that is to also lock cs_main.. but meh

  16. in src/wallet/wallet.h:793 in fba0830bca outdated
     788 | @@ -789,18 +789,21 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
     789 |  
     790 |      void SetNull()
     791 |      {
     792 | -        nWalletVersion = FEATURE_BASE;
     793 | -        nWalletMaxVersion = FEATURE_BASE;
     794 | +        {
     795 | +            LOCK(cs_wallet);
    


    TheBlueMatt commented at 7:43 PM on December 13, 2017:

    Same here. Just add a cs_wallet lock for the whole function.

  17. practicalswift force-pushed on Feb 22, 2018
  18. practicalswift force-pushed on Feb 22, 2018
  19. practicalswift commented at 8:25 PM on February 22, 2018: contributor

    @TheBlueMatt Thanks for reviewing! Feedback addressed. Please re-review :-)

  20. practicalswift force-pushed on Mar 2, 2018
  21. practicalswift commented at 4:58 PM on March 2, 2018: contributor

    Fixed build issue. Please re-review :-)

  22. MarcoFalke commented at 5:05 PM on March 2, 2018: member

    Given that https://github.com/bitcoin/bitcoin/pull/11226/files#diff-12635a58447c65585f51d32b7e04075bR857 is now closed, wouldn't it make sense to add the clang annotations within this commit?

  23. practicalswift force-pushed on Mar 10, 2018
  24. practicalswift force-pushed on Mar 10, 2018
  25. practicalswift force-pushed on Mar 10, 2018
  26. practicalswift force-pushed on Mar 10, 2018
  27. practicalswift commented at 1:27 PM on March 10, 2018: contributor

    @MarcoFalke @TheBlueMatt @promag Thanks for reviewing. I've now addressed the feedback and added corresponding GUARDED_BY/EXCLUSIVE_LOCKS_REQUIRED annotations. Please re-review :-)

  28. in src/wallet/wallet.cpp:3900 in 8dcfcb265d outdated
    3937 | @@ -3930,6 +3938,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
    3938 |      int64_t nStart = GetTimeMillis();
    3939 |      bool fFirstRun = true;
    3940 |      CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path));
    3941 | +    LOCK(walletInstance->cs_wallet);
    3942 |      DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
    


    MarcoFalke commented at 10:12 PM on March 11, 2018:

    Note that LoadWallet takes LOCK2(cs_main, cs_wallet), so taking cs_wallet before cs_main leads to a deadlock in case another thread takes cs_main and then cs_wallet, no?


    practicalswift commented at 3:32 PM on March 12, 2018:

    Thanks. Now fixed. See #11634 (review).

  29. practicalswift force-pushed on Mar 12, 2018
  30. practicalswift force-pushed on Mar 12, 2018
  31. practicalswift commented at 4:02 PM on March 12, 2018: contributor

    Please re-review :-)

  32. practicalswift force-pushed on Mar 14, 2018
  33. practicalswift commented at 7:12 PM on March 14, 2018: contributor

    Rebased!

    Having this merged would have catched this locking incident: https://github.com/bitcoin/bitcoin/pull/12565/files#r171235800

    Reviews welcome! Perhaps @promag or @Sjors could take a look? :-)

  34. in src/wallet/wallet.cpp:1841 in b0cf4aec4d outdated
    1836 | @@ -1837,7 +1837,10 @@ std::set<uint256> CWalletTx::GetConflicts() const
    1837 |      if (pwallet != nullptr)
    1838 |      {
    1839 |          uint256 myHash = GetHash();
    1840 | -        result = pwallet->GetConflicts(myHash);
    1841 | +        {
    1842 | +            LOCK(pwallet->cs_wallet);
    


    promag commented at 9:20 PM on March 14, 2018:

    I believe this lock is not necessary, all callers have the lock already.

  35. Sjors commented at 10:17 PM on March 14, 2018: member

    Concept ACK: anything that prevents me from making mistakes :-)

  36. practicalswift force-pushed on Mar 14, 2018
  37. practicalswift commented at 10:48 PM on March 14, 2018: contributor

    @promag Thanks for reviewing. Feedback addressed. Please re-review :-)

  38. in src/wallet/wallet.cpp:1966 in ce156e6653 outdated
    1934 | @@ -1935,6 +1935,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
    1935 |  
    1936 |      CAmount nCredit = 0;
    1937 |      uint256 hashTx = GetHash();
    1938 | +    LOCK(pwallet->cs_wallet);
    


    promag commented at 11:41 PM on March 14, 2018:

    Unnecessary, callers of GetAvailableCredit already have the lock.


    practicalswift commented at 7:03 AM on March 15, 2018:

    Is pcoin->pwallet->cs_wallet == pwallet->cs_wallet guaranteed?


    promag commented at 12:02 PM on March 15, 2018:

    Yes, otherwise we have a big problem I think 😛


    practicalswift commented at 2:08 PM on March 15, 2018:

    Unfortunately Clang's thread-safety analysis does not seem to understand that :-(


    practicalswift commented at 7:51 AM on March 19, 2018:

    @promag I think I'm unable to resolve this. Are you able to make this work with EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) on CWalletTx::GetAvailableCredit when compiling with Clang's thread-safety analysis enabled?


    MarcoFalke commented at 1:10 PM on August 13, 2018:

    Agree with @promag . We wouldn't want to add LOCKs just to please a static analyser that isn't smart enough to figure out the lock is already taken.

  39. in src/wallet/wallet.cpp:1990 in ce156e6653 outdated
    1978 | @@ -1978,6 +1979,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
    1979 |          return nAvailableWatchCreditCached;
    1980 |  
    1981 |      CAmount nCredit = 0;
    1982 | +    LOCK(pwallet->cs_wallet);
    


    promag commented at 11:42 PM on March 14, 2018:

    Unnecessary, callers of GetAvailableWatchOnlyCredit already have the lock.


    practicalswift commented at 7:52 AM on March 19, 2018:

    @promag See above. In theory you're right, but I've been unable to convince Clang's thread-safety analysis that so is the case in practice. See comment above. I might need some help here :-)

  40. sipa commented at 12:49 AM on March 15, 2018: member

    Concept ACK

  41. practicalswift force-pushed on Mar 23, 2018
  42. practicalswift force-pushed on Mar 29, 2018
  43. practicalswift force-pushed on Apr 9, 2018
  44. practicalswift force-pushed on Apr 9, 2018
  45. practicalswift commented at 1:22 PM on April 9, 2018: contributor

    Rebased!

  46. in src/wallet/wallet.h:711 in 83d2186da4 outdated
     707 | @@ -708,7 +708,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
     708 |      int64_t m_max_keypool_index = 0;
     709 |      std::map<CKeyID, int64_t> m_pool_key_to_index;
     710 |  
     711 | -    int64_t nTimeFirstKey = 0;
     712 | +    int64_t nTimeFirstKey GUARDED_BY(cs_wallet);
    


    MarcoFalke commented at 1:24 PM on April 9, 2018:

    Why remove the initialization?


    practicalswift commented at 1:36 PM on April 9, 2018:

    Ouch, that was not the intention. Now fixed. Thanks for noticing. Please re-review.

  47. practicalswift force-pushed on Apr 9, 2018
  48. practicalswift force-pushed on Apr 9, 2018
  49. practicalswift commented at 10:58 PM on April 9, 2018: contributor

    Rebased!

  50. promag cross-referenced this on Apr 10, 2018 from issue Remove CWallet dependency from CWalletTx by promag
  51. practicalswift cross-referenced this on Apr 30, 2018 from issue Meta-issue: Add Clang thread safety analysis annotations by practicalswift
  52. practicalswift force-pushed on May 5, 2018
  53. in src/wallet/wallet.cpp:322 in 0c6f50067d outdated
     318 | @@ -319,7 +319,7 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne
     319 |   * Update wallet first key creation time. This should be called whenever keys
     320 |   * are added to the wallet, with the oldest key creation time.
     321 |   */
     322 | -void CWallet::UpdateTimeFirstKey(int64_t nCreateTime)
     323 | +void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
    


    MarcoFalke commented at 11:39 PM on May 29, 2018:

    All those are redundant to the header

  54. practicalswift force-pushed on Jun 3, 2018
  55. practicalswift force-pushed on Jun 3, 2018
  56. practicalswift commented at 7:41 AM on June 3, 2018: contributor

    @MarcoFalke Thanks for the review. I've now reworked this PR and moved annotations to the .h files where possible. Could you please re-review? :-)

  57. MarcoFalke commented at 5:18 PM on June 3, 2018: member

    GetConflicts has still an annotation in the cpp file?

    • If it is not trivially possible to move to the header file, better remove the annotation for now.
  58. practicalswift commented at 5:34 PM on June 3, 2018: contributor

    @MarcoFalke

    Applying …

    diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
    index a74efb919..f2926fd74 100644
    --- a/src/wallet/wallet.cpp
    +++ b/src/wallet/wallet.cpp
    @@ -1865,7 +1865,7 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)
         return false;
     }
    
    -std::set<uint256> CWalletTx::GetConflicts() const EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
    +std::set<uint256> CWalletTx::GetConflicts() const
     {
         std::set<uint256> result;
         if (pwallet != nullptr)
    diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
    index d1ffcfbdc..fa4ca5ccb 100644
    --- a/src/wallet/wallet.h
    +++ b/src/wallet/wallet.h
    @@ -493,7 +493,7 @@ public:
         /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
         bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
    
    -    std::set<uint256> GetConflicts() const;
    +    std::set<uint256> GetConflicts() const EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet);
     };
    
     class COutput
    

    … results in …

    In file included from wallet/feebumper.cpp:6:
    In file included from ./wallet/coincontrol.h:11:
    ./wallet/wallet.h:496:76: error: member access into incomplete type 'const CWallet'
        std::set<uint256> GetConflicts() const EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet);
                                                                               ^
    

    OTOH, applying only …

    diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
    index a74efb919..f2926fd74 100644
    --- a/src/wallet/wallet.cpp
    +++ b/src/wallet/wallet.cpp
    @@ -1865,7 +1865,7 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)
         return false;
     }
    
    -std::set<uint256> CWalletTx::GetConflicts() const EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
    +std::set<uint256> CWalletTx::GetConflicts() const
     {
         std::set<uint256> result;
         if (pwallet != nullptr)
    

    … results in …

    wallet/wallet.cpp:1874:27: error: calling function 'GetConflicts' requires holding mutex 'pwallet->cs_wallet' exclusively [-Werror,-Wthread-safety-analysis]
            result = pwallet->GetConflicts(myHash);
                              ^
    
  59. practicalswift commented at 12:53 PM on June 6, 2018: contributor

    @MarcoFalke Another alternative could be to add NO_THREAD_SAFETY_ANALYSIS here to disable the analysis locally.

    What would you suggest as the recommended way to proceed?

  60. MarcoFalke commented at 1:11 PM on June 6, 2018: member

    @practicalswift I like your latest suggestion (NO_THREAD_SAFETY_ANALYSIS). Make sure to include a comment to explain this is only temporary. Also, explain that this is safe to do, since we have a run-time AssertLockHeld in place.

  61. practicalswift force-pushed on Jun 6, 2018
  62. practicalswift force-pushed on Jun 6, 2018
  63. practicalswift force-pushed on Jun 6, 2018
  64. practicalswift force-pushed on Jun 6, 2018
  65. practicalswift force-pushed on Jun 6, 2018
  66. practicalswift commented at 3:16 PM on June 6, 2018: contributor

    @MarcoFalke Good point! Added and documented NO_THREAD_SAFETY_ANALYSIS. Please review :-)

  67. DrahtBot cross-referenced this on Jun 30, 2018 from issue Fix get balance by jnewbery
  68. DrahtBot cross-referenced this on Jul 11, 2018 from issue Free keystore.h from file scope level type aliases by l2a5b1
  69. practicalswift force-pushed on Jul 11, 2018
  70. practicalswift commented at 11:33 PM on July 11, 2018: contributor

    Rebased!

  71. DrahtBot cross-referenced this on Jul 13, 2018 from issue [moveonly] Extract CWallet::MarkInputsDirty, and privatize AddToWalletIfInvolvingMe by Empact
  72. DrahtBot added the label Needs rebase on Jul 14, 2018
  73. practicalswift force-pushed on Jul 15, 2018
  74. practicalswift force-pushed on Jul 15, 2018
  75. practicalswift commented at 7:53 AM on July 16, 2018: contributor

    Rebased!

  76. DrahtBot removed the label Needs rebase on Jul 16, 2018
  77. practicalswift force-pushed on Jul 20, 2018
  78. DrahtBot added the label Needs rebase on Jul 24, 2018
  79. practicalswift force-pushed on Aug 1, 2018
  80. practicalswift commented at 8:33 AM on August 1, 2018: contributor

    Rebased!

  81. DrahtBot removed the label Needs rebase on Aug 1, 2018
  82. DrahtBot cross-referenced this on Aug 1, 2018 from issue [wallet] Kill accounts by jnewbery
  83. DrahtBot cross-referenced this on Aug 1, 2018 from issue wallet: -avoidreuse with destination filters by kallewoof
  84. DrahtBot cross-referenced this on Aug 1, 2018 from issue wallet: "avoid_reuse" wallet flag for improved privacy by kallewoof
  85. practicalswift force-pushed on Aug 6, 2018
  86. practicalswift commented at 4:16 PM on August 6, 2018: contributor

    Rebased!

  87. DrahtBot cross-referenced this on Aug 8, 2018 from issue Always create signatures with Low R values by achow101
  88. practicalswift force-pushed on Aug 13, 2018
  89. practicalswift commented at 10:47 AM on August 13, 2018: contributor

    Rebase number eight performed! :-)

  90. practicalswift force-pushed on Aug 25, 2018
  91. practicalswift commented at 10:44 PM on August 25, 2018: contributor

    Rebased!

  92. practicalswift force-pushed on Aug 26, 2018
  93. practicalswift force-pushed on Aug 26, 2018
  94. practicalswift force-pushed on Aug 26, 2018
  95. practicalswift commented at 3:41 PM on August 26, 2018: contributor

    @TheBlueMatt @promag Feedback addressed (added two commits to keep changes easy to review). @MarcoFalke @sipa @Sjors Please re-review :-)

  96. MarcoFalke commented at 4:10 PM on August 26, 2018: member

    @practicalswift Could squash into two commits? First one is adding the LOCKs, the second one the annotations?

  97. practicalswift force-pushed on Aug 26, 2018
  98. practicalswift force-pushed on Aug 26, 2018
  99. practicalswift force-pushed on Aug 26, 2018
  100. practicalswift commented at 7:52 PM on August 26, 2018: contributor

    @MarcoFalke Done! Please re-review :-)

  101. MarcoFalke commented at 2:04 AM on August 27, 2018: member

    utACK 75cb9c068570ec433f6ebc966f0771caa92efe81

  102. practicalswift force-pushed on Aug 30, 2018
  103. practicalswift commented at 2:57 PM on August 30, 2018: contributor

    Rebased! @MarcoFalke @promag - please re-review your utACK:s :-)

  104. practicalswift cross-referenced this on Aug 31, 2018 from issue wallet: Remove unused local variable old_label by practicalswift
  105. DrahtBot cross-referenced this on Sep 4, 2018 from issue Refactoring: Clarify code using encrypted_batch in CWallet by domob1812
  106. DrahtBot cross-referenced this on Sep 7, 2018 from issue Directly operate with CMutableTransaction by MarcoFalke
  107. DrahtBot cross-referenced this on Sep 7, 2018 from issue Replace coin selection fallback strategy with Single Random Draw by achow101
  108. DrahtBot commented at 1:30 PM on September 21, 2018: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->Reviewers, this pull request conflicts with the following ones:

    • #14498 (rpcwallet: listsentbyaddress RPC by mrwhythat)
    • #14437 (Refactor: Start to separate wallet from node by ryanofsky)
    • #14144 (Refactoring: Clarify code using encrypted_batch in CWallet by domob1812)
    • #13756 (wallet: -avoidreuse feature for improved privacy by kallewoof)
    • #9381 (Remove CWalletTx merging logic from AddToWallet by ryanofsky)

    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.

  109. DrahtBot cross-referenced this on Sep 21, 2018 from issue Refactor: separate wallet from node by ryanofsky
  110. DrahtBot cross-referenced this on Sep 21, 2018 from issue Remove CWalletTx merging logic from AddToWallet by ryanofsky
  111. practicalswift cross-referenced this on Sep 24, 2018 from issue 985d28cc: Facebook Infer Static Analyzer report by practicalswift
  112. DrahtBot cross-referenced this on Sep 27, 2018 from issue Multiprocess bitcoin by ryanofsky
  113. DrahtBot cross-referenced this on Oct 9, 2018 from issue Refactor: Start to separate wallet from node by ryanofsky
  114. wallet: Add missing locks 1c7e25db0c
  115. wallet: Add Clang thread safety analysis annotations dee42927c9
  116. Add GUARDED_BY(cs_wallet) for encrypted_batch, nWalletMaxVersion, m_max_keypool_index and nOrderPosNext
    * AddKeyPubKeyWithDB(...) reads encrypted_batch which potentially races with write in the same method.
    * IncOrderPosNext(...) reads nOrderPosNext which potentially races with write in BlockDisconnected(...).
    * LoadKeyPool(...) reads m_max_keypool_index which potentially races with write in BlockDisconnected(...).
    * LoadMinVersion(...) reads nWalletMaxVersion which potentially races with write in BlockDisconnected(...).
    37b2538c2d
  117. practicalswift force-pushed on Oct 9, 2018
  118. practicalswift commented at 10:19 AM on October 9, 2018: contributor

    Updated!

    Added GUARDED_BY(cs_wallet) annotations also for encrypted_batch, nWalletMaxVersion, m_max_keypool_index and nOrderPosNext.

    Rationale:

    • AddKeyPubKeyWithDB(...) reads encrypted_batch which potentially races with write in the same method
    • IncOrderPosNext(...) reads nOrderPosNext which potentially races with write in BlockDisconnected(...)
    • LoadKeyPool(...) reads m_max_keypool_index which potentially races with write in BlockDisconnected(...)
    • LoadMinVersion(...) reads nWalletMaxVersion which potentially races with write in BlockDisconnected(...)

    Please review :-)

  119. practicalswift commented at 12:00 PM on October 9, 2018: contributor

    Added GUARDED_BY(cs_wallet) for setExternalKeyPool, mapKeyMetadata, m_script_metadata and setLockedCoins.

    Rationale:

    $ git grep -E 'AssertLockHeld.*(setExternalKeyPool|mapKeyMetadata|m_script_metadata|setLockedCoins)' | sort | uniq -c
          4 src/wallet/wallet.cpp:    AssertLockHeld(cs_wallet); // mapKeyMetadata
          1 src/wallet/wallet.cpp:    AssertLockHeld(cs_wallet); // m_script_metadata
          1 src/wallet/wallet.cpp:    AssertLockHeld(cs_wallet); // setExternalKeyPool
          5 src/wallet/wallet.cpp:    AssertLockHeld(cs_wallet); // setLockedCoins
    
  120. Add GUARDED_BY(cs_wallet) for setExternalKeyPool, mapKeyMetadata, m_script_metadata and setLockedCoins 69e7ee2dd8
  121. practicalswift force-pushed on Oct 9, 2018
  122. DrahtBot cross-referenced this on Oct 20, 2018 from issue rpcwallet: listsentbyaddress RPC by rodentrabies
  123. practicalswift commented at 8:35 AM on October 24, 2018: contributor

    @MarcoFalke @promag @TheBlueMatt Would you mind reviewing the updated version? :-)

  124. MarcoFalke commented at 8:52 AM on October 24, 2018: member

    utACK 69e7ee2dd8173597e766262fd9a8caae569ddf5e

  125. MarcoFalke referenced this in commit e895fdc9fc on Oct 24, 2018
  126. MarcoFalke merged this on Oct 24, 2018
  127. MarcoFalke closed this on Oct 24, 2018

  128. in src/wallet/wallet.cpp:4096 in 69e7ee2dd8
    4092 | @@ -4093,7 +4093,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
    4093 |      // Try to top up keypool. No-op if the wallet is locked.
    4094 |      walletInstance->TopUpKeyPool();
    4095 |  
    4096 | -    LOCK(cs_main);
    4097 | +    LOCK2(cs_main, walletInstance->cs_wallet);
    


    promag commented at 9:03 AM on October 24, 2018:

    No annotation requires this lock right?


    practicalswift commented at 9:28 AM on October 24, 2018:

    Both cs_main and walletInstance->cs_wallet are required from what I can tell.

    Removing that lock results in the following:

    wallet/wallet.cpp:4104:28: error: calling function 'FindForkInGlobalIndex' requires holding mutex 'cs_main' exclusively [-Werror,-Wthread-safety-analysis]
                pindexRescan = FindForkInGlobalIndex(chainActive, locator);
                               ^
    wallet/wallet.cpp:4131:48: error: reading variable 'nTimeFirstKey' requires holding mutex 'walletInstance->cs_wallet' [-Werror,-Wthread-safety-analysis]
            while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
                                                   ^
    wallet/wallet.cpp:4131:114: error: reading variable 'nTimeFirstKey' requires holding mutex 'walletInstance->cs_wallet' [-Werror,-Wthread-safety-analysis]
            while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
                                                                                                                     ^
    wallet/wallet.cpp:4156:77: error: reading variable 'mapWallet' requires holding mutex 'walletInstance->cs_wallet' [-Werror,-Wthread-safety-analysis]
                    std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);
                                                                                ^
    wallet/wallet.cpp:4157:43: error: reading variable 'mapWallet' requires holding mutex 'walletInstance->cs_wallet' [-Werror,-Wthread-safety-analysis]
                    if (mi != walletInstance->mapWallet.end())
                                              ^
    wallet/wallet.cpp:4181:90: error: calling function 'GetKeyPoolSize' requires holding mutex 'walletInstance->cs_wallet' exclusively [-Werror,-Wthread-safety-analysis]
            walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n",      walletInstance->GetKeyPoolSize());
                                                                                             ^
    wallet/wallet.cpp:4182:90: error: reading variable 'mapWallet' requires holding mutex 'walletInstance->cs_wallet' [-Werror,-Wthread-safety-analysis]
            walletInstance->WalletLogPrintf("mapWallet.size() = %u\n",       walletInstance->mapWallet.size());
                                                                                             ^
    

    promag commented at 9:37 AM on October 24, 2018:

    Right, the new annotations..

    It could make sense to avoid calling uiInterface.LoadWallet() with the lock held.


    practicalswift commented at 9:56 AM on October 24, 2018:

    @promag Ah, now I follow. Suggested fix below. What do you think?

    diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
    index 29014790e..2deeb9c42 100644
    --- a/src/wallet/wallet.cpp
    +++ b/src/wallet/wallet.cpp
    @@ -4093,7 +4093,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
         // Try to top up keypool. No-op if the wallet is locked.
         walletInstance->TopUpKeyPool();
    
    -    LOCK2(cs_main, walletInstance->cs_wallet);
    +    LOCK(cs_main);
    
         CBlockIndex *pindexRescan = chainActive.Genesis();
         if (!gArgs.GetBoolArg("-rescan", false))
    @@ -4128,6 +4128,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
    
             // No need to read and scan block if block was created before
             // our wallet birthday (as adjusted for block time variability)
    +        LOCK(walletInstance->cs_wallet);
             while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
                 pindexRescan = chainActive.Next(pindexRescan);
             }
    @@ -4178,6 +4179,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
         walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
    
         {
    +        LOCK(walletInstance->cs_wallet);
             walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n",      walletInstance->GetKeyPoolSize());
             walletInstance->WalletLogPrintf("mapWallet.size() = %u\n",       walletInstance->mapWallet.size());
             walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n",  walletInstance->mapAddressBook.size());
    

    promag commented at 10:08 AM on October 24, 2018:

    I think that currently there is no harm in calling uiInterface.LoadWallet() with the lock held but if it's not necessary then I wouldn't change that. The diff looks ok.

  129. promag commented at 9:09 AM on October 24, 2018: member

    utACK 69e7ee2.

  130. practicalswift cross-referenced this on Oct 24, 2018 from issue wallet: Avoid calling uiInterface.LoadWallet(...) with cs_wallet held by practicalswift
  131. LarryRuane referenced this in commit e1c6c472e9 on Apr 5, 2021
  132. LarryRuane referenced this in commit d5439c7386 on Apr 5, 2021
  133. LarryRuane referenced this in commit affdf23507 on Apr 5, 2021
  134. LarryRuane referenced this in commit ed497c99af on Apr 5, 2021
  135. LarryRuane cross-referenced this on Apr 5, 2021 from issue Bitcoin 0.18 locking PRs by LarryRuane
  136. practicalswift deleted the branch on Apr 10, 2021
  137. LarryRuane referenced this in commit b2cdcd07c1 on Apr 29, 2021
  138. LarryRuane referenced this in commit a0df9acb00 on Apr 29, 2021
  139. LarryRuane referenced this in commit 10f07f2ad1 on Apr 29, 2021
  140. LarryRuane referenced this in commit 973d009148 on Apr 29, 2021
  141. LarryRuane referenced this in commit 20ce9cce7e on Jun 1, 2021
  142. LarryRuane referenced this in commit 12a307c6e1 on Jun 1, 2021
  143. LarryRuane referenced this in commit 96b1669b6d on Jun 1, 2021
  144. LarryRuane referenced this in commit 4bae811116 on Jun 1, 2021
  145. PastaPastaPasta referenced this in commit dab5cfb8ca on Jul 17, 2021
  146. ryanofsky cross-referenced this on Nov 24, 2021 from issue wallet: Call load handlers without cs_wallet locked by promag
  147. gades referenced this in commit a188a9ea87 on May 31, 2022
  148. bitcoin locked this on Nov 22, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-19 06:54 UTC