[bugfix] wallet: Fix duplicate fileid detection #14320

pull ken2812221 wants to merge 2 commits into bitcoin:master from ken2812221:2018-09-25-wallet-duplicate-fileid-fix changing 3 files +30 −12
  1. ken2812221 commented at 2:42 PM on September 25, 2018: contributor

    The implementation in current master can not detect if the file ID is duplicate with flushed BerkeleyEnvironment. This PR would store the file ID in a global variable g_fileids and release it when the BerkeleyDatabase close. So it won't have to rely on a Db*.

    Fix #14304

  2. ken2812221 force-pushed on Sep 25, 2018
  3. in src/wallet/db.cpp:28 in 4d5a58b1d2 outdated
      20 | @@ -20,6 +21,12 @@
      21 |  #include <boost/thread.hpp>
      22 |  
      23 |  namespace {
      24 | +
      25 | +struct BerkeleyFileid {
      26 | +    u_int8_t value[DB_FILE_ID_LEN];
      27 | +};
      28 | +std::unordered_map<std::string, BerkeleyFileid> g_fileids;
    


    ryanofsky commented at 3:32 PM on September 25, 2018:

    I think it'd be more correct to add a std::unordered_map<std::string, BerkeleyFileid> m_fileids member in BerkeleyEnvironment member instead of a g_fileids global because there can be duplicate filenames if the files are in different directories.

    Also, making this a member would be consistent with existing mapFileUseCount mapDb members there which are also maps indexed by filename. In the future it could be nice to consolidate the 3 maps.


    ken2812221 commented at 4:38 PM on September 25, 2018:

    Updated

  4. ryanofsky commented at 3:36 PM on September 25, 2018: contributor

    Great catch, I was surprised to see this bug. It seems like the problem here is that BerkeleyEnvironment::Flush actually fully unloads databases, so the check for duplicate fileids when opening a new database doesn't work at all after flushing.

    I wonder if you could make a python test case out of the steps in #14304.

  5. ken2812221 force-pushed on Sep 25, 2018
  6. DrahtBot commented at 5:01 PM on September 25, 2018: contributor

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

    • #14531 (Replace fs::relative call with custom GetRelativePath by promag)
    • #11911 (Free BerkeleyEnvironment instances when not in use 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.

  7. DrahtBot cross-referenced this on Sep 25, 2018 from issue tests: exclude all tests with difference parameters in `--exclude` list by ken2812221
  8. in src/wallet/db.cpp:43 in 839142c826 outdated
      48 | -        if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
      49 | -            memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
      50 | -            const char* item_filename = nullptr;
      51 | -            item.second->get_dbname(&item_filename, nullptr);
      52 | +    for (const auto& item : env.m_fileids) {
      53 | +        if (item.first != filename && memcmp(fileid.value, item.second.value, sizeof(fileid)) == 0) {
    


    promag commented at 5:10 PM on September 25, 2018:

    move memcmp to BerkeleyFileid::operator==?


    ken2812221 commented at 8:31 PM on September 25, 2018:

    done

  9. DrahtBot cross-referenced this on Sep 25, 2018 from issue tests: Write the notification message to different files to avoid race condition in feature_notifications.py by ken2812221
  10. in src/wallet/db.cpp:50 in 0ce468502b outdated
      56 | -                item_filename ? item_filename : "(unknown database)"));
      57 | +                HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
      58 |          }
      59 |      }
      60 | +
      61 | +    env.m_fileids.emplace(filename, fileid);
    


    ryanofsky commented at 5:45 PM on September 25, 2018:

    Since CheckUniqueFileid is called on all BerkeleyEnvironment instances, this will incorrectly add the filename to unrelated BerkeleyEnvironment instances that don't actually contain filename. I'd suggest dropping this line, adding a BerkeleyFileid& fileid output parameter to CheckUniqueFileid, and passing in this->env->m_fileids[strFilename] where CheckUniqueFileid is called in BerkeleyBatch::BerkeleyBatch.


    ken2812221 commented at 8:32 PM on September 25, 2018:

    done

  11. in src/fs.cpp:92 in 87a39fcdd6 outdated
      88 | @@ -89,7 +89,7 @@ bool FileLock::TryLock()
      89 |          return false;
      90 |      }
      91 |      _OVERLAPPED overlapped = {0};
      92 | -    if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 0, &overlapped)) {
      93 | +    if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, -1, -1, &overlapped)) {
    


    ryanofsky commented at 5:53 PM on September 25, 2018:

    In commit "util: Fix broken Windows file lock"

    Can you describe how the lock is broken in the commit message?


    ken2812221 commented at 7:42 PM on September 25, 2018:

    That was introduced by #13862. I assume that it could work with length 0 if it is a exclusive lock, but it doesn't. I would have to specify -1, -1 to make it work.

    The boost implementation:

    https://github.com/boostorg/interprocess/blob/8b3621353017aa527a22124655c9458d0b64b358/include/boost/interprocess/detail/os_file_functions.hpp#L230-L243

  12. ken2812221 force-pushed on Sep 25, 2018
  13. DrahtBot cross-referenced this on Sep 25, 2018 from issue Free BerkeleyEnvironment instances when not in use by ryanofsky
  14. ken2812221 force-pushed on Sep 25, 2018
  15. ken2812221 force-pushed on Sep 25, 2018
  16. in src/wallet/db.cpp:826 in f31c3fcc88 outdated
     825 | @@ -826,6 +826,8 @@ void BerkeleyDatabase::Flush(bool shutdown)
     826 |              LOCK(cs_db);
    


    ken2812221 commented at 7:52 PM on September 25, 2018:

    Note that if two or more BerkeleyDatabase share the same BerkeleyEnvironment, the env would be a wild pointer after the first call.


    ryanofsky commented at 8:25 PM on September 25, 2018:

    Good catch. This appears to be a bug introduced in 0b82bac76d0f842bd2294a290388536951fbc576 from #13111 and then tweaked in a769461d5e37ddcb771ae836254fdc69177a28c4 in #12493. It seems not directly related to this issue, so probably best addressed separately.


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

    Good catch indeed. The bug was not introduced in 0b82bac but in a769461d?


    ryanofsky commented at 3:30 PM on October 24, 2018:

    Thread #14320 (review)

    Good catch indeed. The bug was not introduced in 0b82bac but in a769461?

    0b82bac76d0f842bd2294a290388536951fbc576 added the call to erase the environment too early and a769461d5e37ddcb771ae836254fdc69177a28c4 just moved the call, IIUC.

  17. in src/wallet/db.cpp:506 in f31c3fcc88 outdated
     502 | @@ -503,8 +503,8 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
     503 |              // be implemented, so no equality checks are needed at all. (Newer
     504 |              // versions of BDB have an set_lk_exclusive method for this
     505 |              // purpose, but the older version we use does not.)
     506 | -            for (const auto& env : g_dbenvs) {
     507 | -                CheckUniqueFileid(env.second, strFilename, *pdb_temp);
     508 | +            for (auto& env : g_dbenvs) {
    


    ryanofsky commented at 8:10 PM on September 25, 2018:

    I think you could restore auto& env to const auto& env here.

  18. ken2812221 force-pushed on Sep 25, 2018
  19. in src/wallet/db.cpp:43 in 8389ce8fb0 outdated
      47 | -        if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
      48 | -            memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
      49 | -            const char* item_filename = nullptr;
      50 | -            item.second->get_dbname(&item_filename, nullptr);
      51 | +    for (const auto& item : env.m_fileids) {
      52 | +        if (item.first != filename && fileid == item.second) {
    


    ryanofsky commented at 8:19 PM on September 25, 2018:

    I think item.first != filename condition makes the check too loose again because the same filename can exist in different directories (and we'd expect pretty much all wallet directories to have a wallet.dat file).

    Perhaps change condition to: if (item.second == fileid && !(&item.second == &fileid))

  20. in src/wallet/db.cpp:56 in 8389ce8fb0 outdated
      50 | @@ -56,6 +51,11 @@ CCriticalSection cs_db;
      51 |  std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
      52 |  } // namespace
      53 |  
      54 | +bool BerkeleyFileid::operator==(const BerkeleyFileid& b) const
      55 | +{
      56 | +    return memcmp(this, &b, sizeof(BerkeleyFileid)) == 0;
    


    ryanofsky commented at 8:20 PM on September 25, 2018:

    It might be safer if memcmp compared just the value field rather than the whole object, since it seems possible the compiler could pad the objects. Maybe:

    return memcmp(this->field, b.field, sizeof(this->field)) == 0;
    
  21. fanquake added the label Wallet on Sep 25, 2018
  22. ken2812221 force-pushed on Sep 26, 2018
  23. in src/wallet/db.cpp:829 in cb4af1969c outdated
     825 | @@ -826,6 +826,8 @@ void BerkeleyDatabase::Flush(bool shutdown)
     826 |              LOCK(cs_db);
     827 |              g_dbenvs.erase(env->Directory().string());
     828 |              env = nullptr;
     829 | +        } else {
    


    ryanofsky commented at 6:22 AM on September 26, 2018:

    Why not call m_fileds.erase if shutdown is true? It seems ok to only erase if shutdown is false, but if there is a specific reason for not erasing when shutting down, it would be useful to note in a code comment.


    ken2812221 commented at 6:32 AM on September 26, 2018:

    That would cause an access violation on Windows since it's a wild pointer in the second call. Also, since the env is deleted when g_dbenvs.erase called, the map doesn't exist anymore.

  24. ryanofsky commented at 6:26 AM on September 26, 2018: contributor

    utACK cb4af1969c03b56bc18b63286627922db86743d2. Thanks for the fix and all the followup!

  25. ken2812221 force-pushed on Sep 26, 2018
  26. ken2812221 renamed this:
    wallet: Fix duplicate fileid detection
    [bugfix] wallet: Fix duplicate fileid detection
    on Sep 27, 2018
  27. MarcoFalke commented at 4:20 PM on September 27, 2018: member

    Is this for backport?

  28. ken2812221 force-pushed on Sep 27, 2018
  29. ryanofsky commented at 4:35 PM on September 27, 2018: contributor

    Is this for backport?

    Could be backported, but I think the benefit would be very minimal. I think you have to literally copy a wallet database file and use the loadwallet RPC in order to trigger the missing error check that this PR restores.

  30. ryanofsky commented at 8:49 PM on September 27, 2018: contributor

    utACK f936b8d14bfa8c758984430181db20514d34accd. Only change since previous review is adding code comment.

  31. DrahtBot commented at 3:33 AM on September 28, 2018: contributor

    <!--32850dd3fdea838b4049e64f46995ea2-->

    Coverage Change (pull 14320) Reference (master)
    Lines +0.0019 % 87.0361 %
    Functions -0.0049 % 84.1130 %
    Branches -0.0008 % 51.5451 %
  32. DrahtBot cross-referenced this on Oct 9, 2018 from issue utils: Fix broken Windows filelock by ken2812221
  33. DrahtBot added the label Needs rebase on Oct 20, 2018
  34. ken2812221 force-pushed on Oct 21, 2018
  35. ken2812221 commented at 7:47 AM on October 21, 2018: contributor

    Rebased

  36. DrahtBot removed the label Needs rebase on Oct 21, 2018
  37. ken2812221 force-pushed on Oct 21, 2018
  38. ken2812221 force-pushed on Oct 21, 2018
  39. ken2812221 force-pushed on Oct 21, 2018
  40. ken2812221 force-pushed on Oct 21, 2018
  41. ken2812221 force-pushed on Oct 21, 2018
  42. ken2812221 force-pushed on Oct 21, 2018
  43. ken2812221 force-pushed on Oct 21, 2018
  44. ken2812221 cross-referenced this on Oct 22, 2018 from issue loadwallet command crashes bitcoind by chernyshev
  45. practicalswift cross-referenced this on Oct 22, 2018 from issue Enable flake8 rule E225 which checks for missing whitespace around op… by jbampton
  46. in test/functional/wallet_multiwallet.py:78 in 02b6bd343d outdated
      74 |              wallet_names.remove('w7_symlink')
      75 | +            wallet_exclude = {'w7'}
      76 |          extra_args = ['-wallet={}'.format(n) for n in wallet_names]
      77 |          self.start_node(0, extra_args)
      78 | -        assert_equal(set(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), set(['', 'w3', 'w2', 'sub/w5', 'w7', 'w7', 'w1', 'w8', 'w']))
      79 | +        assert_equal(set(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), set(['', 'w3', 'w2', os.path.join('sub', 'w5'), 'w7', 'w7', 'w1', 'w8', 'w']) - wallet_exclude)
    


    ryanofsky commented at 9:11 PM on October 22, 2018:

    Note: It looks os.path.join change is not directly related to this PR, but is just a fix for listwalletdir (added in #14291) on windows.


    ryanofsky commented at 9:12 PM on October 22, 2018:

    'w7', 'w7'

    Code precedes this PR, but I think listing w7 twice has no effect.


    promag commented at 10:14 PM on October 22, 2018:

    This is changed in #14531.

  47. ryanofsky approved
  48. ryanofsky commented at 9:15 PM on October 22, 2018: contributor

    utACK 02b6bd343da296dc29ea017447b18c9afa13b5e3. Only changes since last review are rebase and listwalletdir fixes.

  49. DrahtBot cross-referenced this on Oct 23, 2018 from issue Replace fs::relative call with custom GetRelativePath by promag
  50. ken2812221 force-pushed on Oct 24, 2018
  51. ken2812221 cross-referenced this on Oct 24, 2018 from issue appveyor: Enable multiwallet tests by ken2812221
  52. ken2812221 commented at 7:13 AM on October 24, 2018: contributor

    Just split appveyor part to #14559 to make this PR easier to review.

  53. in src/wallet/db.cpp:835 in ef16fc5da6 outdated
     825 | @@ -826,6 +826,10 @@ void BerkeleyDatabase::Flush(bool shutdown)
     826 |              LOCK(cs_db);
     827 |              g_dbenvs.erase(env->Directory().string());
     828 |              env = nullptr;
     829 | +        } else {
     830 | +            // To avoid accessing a map that has already deconstructed, do not call this when shutdown is true. g_dbenvs.erase would also erase this value.
     831 | +            // TODO: get rid of wild pointers
     832 | +            env->m_fileids.erase(strFile);
    


    ryanofsky commented at 2:23 PM on October 24, 2018:

    It might not be clear what "wild pointers" refers to in this todo (laanwj asked about it here: #14531 (review)). Wild pointers could mean either dead pointers or raw pointers, but I think the TODO is referring to the bug described here: #14320 (review). Maybe could be expanded as:

    // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
    // first database shutdown when multiple databases are open in the same
    // environment, should replace raw database `env` pointers with shared or weak
    // pointers, or else separate the database and environment shutdowns so
    // environments can be shut down after databases.
    
  54. ryanofsky commented at 2:34 PM on October 24, 2018: contributor

    utACK ef16fc5da6ca6b33d4a92e509d627d908f8ed995. Only change since last review is dropping appveyor commit and related wallet_multiwallet.py test fixes.

  55. in src/wallet/db.h:29 in ef16fc5da6 outdated
      24 |  #include <db_cxx.h>
      25 |  
      26 |  static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
      27 |  static const bool DEFAULT_WALLET_PRIVDB = true;
      28 |  
      29 | +struct BerkeleyFileid {
    


    promag commented at 2:35 PM on October 24, 2018:

    @laanwj suggested BerkeleyFileId. I also suggest WalletDatabaseFileId or DBFileId.

  56. ken2812221 force-pushed on Oct 24, 2018
  57. wallet: Fix duplicate fileid 2d796faf62
  58. tests: add test case for loading copied wallet twice 4ea77320c5
  59. ken2812221 force-pushed on Oct 24, 2018
  60. ken2812221 commented at 3:13 PM on October 24, 2018: contributor

    ef16fc5 -> 4ea7732

    1. Rename BerkeleyFileid -> WalletDatabaseFileId (@promag)
    2. Added @ryanofsky's comments
  61. ryanofsky approved
  62. ryanofsky commented at 3:18 PM on October 24, 2018: contributor

    utACK 4ea77320c5f0b275876be41ff530bb328ba0cb87. Only changes are fileid class rename and todo comment.

  63. promag commented at 3:32 PM on October 24, 2018: member

    Tested ACK 4ea7732, new test fails without this fix.

  64. laanwj commented at 3:37 PM on October 24, 2018: member

    utACK 6241eb3224d95ea04c04dcab2dac88687f49440e

  65. laanwj merged this on Oct 24, 2018
  66. laanwj closed this on Oct 24, 2018

  67. laanwj referenced this in commit 2b88f67e0b on Oct 24, 2018
  68. ken2812221 deleted the branch on Oct 24, 2018
  69. MarcoFalke referenced this in commit 613fc95ee4 on Oct 24, 2018
  70. ryanofsky cross-referenced this on Oct 25, 2018 from issue Crash with loading wallet async via RPC by furqansiddiqui
  71. ryanofsky cross-referenced this on Nov 15, 2018 from issue wallet: detecting duplicate wallet by comparing the db filename. by ken2812221
  72. MarcoFalke cross-referenced this on Nov 20, 2018 from issue wallet: heap-use-after-free on multiwallet shutdown by MarcoFalke
  73. promag referenced this in commit 0e2d070905 on Mar 11, 2019
  74. promag referenced this in commit 03a9a82240 on Mar 11, 2019
  75. promag cross-referenced this on Mar 11, 2019 from issue 0.17: Backport 15297 by promag
  76. promag referenced this in commit 8965b6ab47 on Mar 11, 2019
  77. promag referenced this in commit 34da2b7c76 on Mar 11, 2019
  78. laanwj referenced this in commit 6cf81b01b4 on Mar 20, 2019
  79. sidhujag referenced this in commit f77f29313e on Mar 28, 2019
  80. sidhujag referenced this in commit dfdc72e534 on Mar 28, 2019
  81. sidhujag referenced this in commit 3ee64a0aa7 on Mar 28, 2019
  82. uhliksk referenced this in commit f3b3b2bbd0 on Apr 21, 2019
  83. uhliksk referenced this in commit 92b54286b0 on Apr 21, 2019
  84. Rishabh42 referenced this in commit ce3547fae6 on Apr 22, 2019
  85. uhliksk referenced this in commit cd29398e08 on May 1, 2019
  86. uhliksk referenced this in commit 6396a64765 on May 1, 2019
  87. jasonbcox referenced this in commit 8915b2f748 on Oct 25, 2019
  88. ryanofsky cross-referenced this on May 29, 2020 from issue wallet: Refactor the classes in wallet/db.{cpp/h} by achow101
  89. xdustinface referenced this in commit 3738170471 on Apr 4, 2021
  90. xdustinface referenced this in commit dc09345e28 on Apr 4, 2021
  91. xdustinface referenced this in commit 85349ca1f7 on Apr 4, 2021
  92. xdustinface referenced this in commit 86357cc55f on Apr 5, 2021
  93. xdustinface referenced this in commit 3bee6d011c on Apr 13, 2021
  94. 5tefan referenced this in commit 12d24c70f4 on Aug 14, 2021
  95. bitcoin locked this on Sep 8, 2021

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-20 06:54 UTC