dbwrapper: properly destroy objects in case CDBWrapper ctor throws #22663

pull Crypt-iQ wants to merge 2 commits into bitcoin:master from Crypt-iQ:unique_ptr_options_08072021 changing 2 files +35 −34
  1. Crypt-iQ commented at 0:42 am on August 8, 2021: contributor

    There are two main changes: introduce LevelDbOptions to manage the leveldb::Options struct and changing CDBWrapper’s penv member to a unique_ptr. The first allows LevelDbOptions to destroy leveldb::Options members without CDBWrapper having to directly. The second change allows the leveldb::Env to be managed by penv. Both of these changes prevent a LeakSanitizer exception if the CDBWrapper ctor throws.

    Fixes #22592

  2. DrahtBot added the label UTXO Db and Indexes on Aug 8, 2021
  3. bitcoin deleted a comment on Aug 8, 2021
  4. DrahtBot commented at 5:54 am on August 8, 2021: contributor

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #25862 (refactor, kernel: Remove gArgs accesses from dbwrapper and txdb by ryanofsky)
    • #25740 (assumeutxo: background validation completion by jamesob)
    • #25667 (assumeutxo: snapshot initialization by jamesob)

    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.

  5. Crypt-iQ commented at 12:48 pm on August 8, 2021: contributor
    One build is failing due to a leak while fuzzing coins_view and I cannot see why with the CI. I am unable to reproduce this on my machine.
  6. in src/dbwrapper.cpp:151 in 6770f066b6 outdated
    162         LogPrintf("Opening LevelDB in %s\n", path.string());
    163     }
    164-    leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
    165+    leveldb::Status status = leveldb::DB::Open(*options, path.string(), &pdb);
    166+    if (!status.ok()) {
    167+        delete penv;
    


    Talkless commented at 3:06 pm on August 10, 2021:
    If penv is deleted, then ~CDBWrapper() gonna delete it again? Missing = nullptr?

    Crypt-iQ commented at 3:13 pm on August 10, 2021:
    If !status.ok then HandleError throws and the destructor isn’t called

    Talkless commented at 12:30 pm on August 11, 2021:

    Maybe worth making penv a unique_ptr too, if we need manual handling with delete? Or do you think this would be kinda out of scope for this PR?

    Adding addition delete into new code does not seem… “right” (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-newdelete).


    Crypt-iQ commented at 7:02 pm on August 11, 2021:
    good point, I can look into making it a unique_ptr

    ryanofsky commented at 7:58 pm on October 29, 2021:

    re: #22663 (review)

    If penv is deleted, then ~CDBWrapper() gonna delete it again? Missing = nullptr?

    In commit “dbwrapper: wrap options in unique_ptr and use DeleteOptions” (6770f066b69c6c271e7096cc647d5d42762f6b05)

    It does seem fragile to rely on HandleError bypassing the destructor to avoid a double-delete. Would second the suggestion to add = nullptr to make the destructor safe if it were called. Or could add a “// Deleting env here without resetting it is safe because the destructor will never be called” comment to clarify. Or could make env a unique_ptr, which also seemed like a good suggestion


    Crypt-iQ commented at 4:02 pm on December 20, 2021:
    The latest change makes penv a unique_ptr
  7. DrahtBot added the label Needs rebase on Oct 15, 2021
  8. in src/dbwrapper.cpp:111 in 6770f066b6 outdated
    118+        options->paranoid_checks = true;
    119     }
    120-    SetMaxOpenFiles(&options);
    121-    return options;
    122+    SetMaxOpenFiles(options);
    123+}
    


    ryanofsky commented at 7:41 pm on October 29, 2021:

    In commit “dbwrapper: wrap options in unique_ptr and use DeleteOptions” (6770f066b69c6c271e7096cc647d5d42762f6b05)

    It’s not too important, but I don’t think there’s an actual need to change this function, and the previous version of this that just returned a struct instead of dereferencing a pointer seemed more straightforward.

    To keep the previous version, I believe you would just need to write new leveldb::Options{GetOptions(...)} below.

    Would be nice to add [[nodiscard]] to the previous version, if you did keep it though.


    laanwj commented at 3:41 pm on November 10, 2021:
    I agree with @ryanofsky. I don’t think there’s a strong need to change this function, but if there is, I would prefer it to return the target type std::unique_ptr<leveldb::Options, void (*)(leveldb::Options*) directly, instead of using a pointer positional out argument (which, strictly, seems a deterioration of API clarity to me).

    Crypt-iQ commented at 1:49 am on November 11, 2021:
    Will work on a patch with this, thanks for the review

    Crypt-iQ commented at 4:03 pm on December 20, 2021:
    Latest change lifts the GetOptions logic into the ctor of LevelDbOptions
  9. in src/dbwrapper.cpp:124 in 6770f066b6 outdated
    129+    options->filter_policy = nullptr;
    130+    delete options->info_log;
    131+    options->info_log = nullptr;
    132+    delete options->block_cache;
    133+    options->block_cache = nullptr;
    134+    options->env = nullptr;
    


    ryanofsky commented at 7:51 pm on October 29, 2021:

    In commit “dbwrapper: wrap options in unique_ptr and use DeleteOptions” (6770f066b69c6c271e7096cc647d5d42762f6b05)

    It seems dodgy to set env to null without freeing it here. I think it would be better to either:

    • Set this to null immediately in the places where the env is deleted, in which case you could assert(!options->env) here
    • Or, to just drop this line and treat env and options lifetimes as more independent
    • Or, to add a comment like “env was previously deleted and should not be freed here” here to make it clearer the inconsistency here is intentional

    Crypt-iQ commented at 4:04 pm on December 20, 2021:
    I opted for the second approach and dropped the line
  10. in src/dbwrapper.cpp:178 in 6770f066b6 outdated
    182@@ -170,14 +183,7 @@ CDBWrapper::~CDBWrapper()
    183 {
    184     delete pdb;
    


    ryanofsky commented at 8:00 pm on October 29, 2021:

    In commit “dbwrapper: wrap options in unique_ptr and use DeleteOptions” (6770f066b69c6c271e7096cc647d5d42762f6b05)

    I liked the suggestion to make env a unique_ptr #22663 (review), and maybe pdb could be a unique_ptr too?


    Crypt-iQ commented at 4:06 pm on December 20, 2021:
    I was not sure how to make pdb a unique_ptr and also pass it in to Open as leveldb::DB** when it is nullptr at this point. Maybe I am missing something.

    ryanofsky commented at 1:30 am on April 27, 2022:

    re: #22663 (review)

    I was not sure how to make pdb a unique_ptr and also pass it in to Open as leveldb::DB** when it is nullptr at this point. Maybe I am missing something.

    No need to do this, but you would need to pass a temporary pointer to Open, and switch to unique_ptr after it returns:

    0leveldb::DB* new_db = nullptr;
    1leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(path), &new_db);
    2pdb.reset(new_db);
    
  11. in src/dbwrapper.cpp:116 in 6770f066b6 outdated
    121-    return options;
    122+    SetMaxOpenFiles(options);
    123+}
    124+
    125+// Custom deleter for unique_ptr<leveldb::Options>.
    126+static void DeleteOptions(leveldb::Options* options)
    


    ryanofsky commented at 8:07 pm on October 29, 2021:

    In commit “dbwrapper: wrap options in unique_ptr and use DeleteOptions” (6770f066b69c6c271e7096cc647d5d42762f6b05)

    Custom deleter seems to be forgetting to delete options, which would explain the fuzzing leak? #22663 (comment)


    Crypt-iQ commented at 4:07 pm on December 20, 2021:
    No longer using a custom deleter and instead using ~LevelDbOptions
  12. ryanofsky approved
  13. ryanofsky commented at 8:14 pm on October 29, 2021: contributor

    6770f066b69c6c271e7096cc647d5d42762f6b05 looks pretty good if the leak is fixed. It generally seems to moves things in a good direction.

    I do think more ideally, if the goal is to make the options struct more RAII-like and exception-safe, instead of requiring it to be allocated separately on the heap and requiring it to be paired with a custom deleter, it would be better to just inherit from it and write a normal destructor:

    0struct LevelDbOptions : public leveldb::Options {
    1  ~LevelDbOptions() {
    2    delete filter_policy;
    3    delete info_log;
    4    ...
    5  }
    6}
    
  14. laanwj commented at 7:52 pm on December 15, 2021: member
    @Crypt-iQ can you please respond to @ryanofsky’s comments and rebase ?
  15. Crypt-iQ commented at 9:31 pm on December 15, 2021: contributor
    Hi yes, I can get to it this weekend
  16. Crypt-iQ force-pushed on Dec 19, 2021
  17. DrahtBot removed the label Needs rebase on Dec 19, 2021
  18. Crypt-iQ renamed this:
    dbwrapper: wrap options in unique_ptr and use DeleteOptions
    dbwrapper: properly destroy objects in case CDBWrapper ctor throws
    on Dec 20, 2021
  19. Crypt-iQ commented at 4:07 pm on December 20, 2021: contributor
    I’ve rebased and hopefully addressed all of the comments
  20. in src/dbwrapper.cpp:100 in 7ba6aa28c7 outdated
    103-    options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
    104-    options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
    105-    options.filter_policy = leveldb::NewBloomFilterPolicy(10);
    106-    options.compression = leveldb::kNoCompression;
    107-    options.info_log = new CBitcoinLevelDBLogger();
    108+LevelDbOptions::LevelDbOptions() {
    


    maflcko commented at 4:07 pm on December 20, 2021:
    0LevelDbOptions::LevelDbOptions()
    1{
    

    nit: clang-format

    You may install clang-format and run the https://github.com/bitcoin/bitcoin/tree/master/contrib/devtools#clang-format-diffpy script to preempt whitespace nitpicking.


    Crypt-iQ commented at 8:15 pm on February 5, 2022:
    fixed
  21. Crypt-iQ force-pushed on Feb 5, 2022
  22. in src/dbwrapper.h:28 in 541b66fa0e outdated
    23+    LevelDbOptions();
    24+
    25+    ~LevelDbOptions();
    26+
    27+    /** Sets block_cache and write_buffer_size since nCacheSize isn't known when
    28+     * LevelDbOptions ctor is called.
    


    laanwj commented at 12:37 pm on April 6, 2022:

    I don’t think this is strictly true? Or am I missing something? nCacheSize is passed into CDBWrapper::CDBWrapper, so it’s absolutely known at the beginning, couldn’t you pass it in like

    0CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
    1    : m_name{fs::PathToString(path.stem())},
    2      options(nCacheSize)
    3
    4
    5struct LevelDbOptions : public leveldb::Options {
    6    LevelDbOptions(size_t nCacheSize);
    

    Crypt-iQ commented at 4:57 pm on April 6, 2022:
    Not sure why I did that, updated to your suggestion

    laanwj commented at 8:49 am on May 11, 2022:
    Thanks!
  23. laanwj changes_requested
  24. Crypt-iQ force-pushed on Apr 6, 2022
  25. ryanofsky requested review from laanwj on Apr 27, 2022
  26. ryanofsky approved
  27. ryanofsky commented at 1:48 am on April 27, 2022: contributor
    Code review ACK 2a5d230c0fd764004a8c83c022ba71d0ee95d2a3. This all looks good and is straightforward. Thanks for implementing all the suggested changes
  28. in src/dbwrapper.h:187 in 2a5d230c0f outdated
    183@@ -178,10 +184,10 @@ class CDBWrapper
    184     friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
    185 private:
    186     //! custom environment this database is using (may be nullptr in case of default environment)
    187-    leveldb::Env* penv;
    188+    std::unique_ptr<leveldb::Env> penv;
    


    maflcko commented at 8:56 am on May 3, 2022:
    nit: Maybe rename to m_env while touching all lines? Maybe split the penv change into a separate commit for easier review?

    Crypt-iQ commented at 10:39 pm on May 5, 2022:
    renamed and split off into separate commit

    Crypt-iQ commented at 1:11 am on May 6, 2022:
    I didn’t functionally change the code and now there is a TSAN failure

    maflcko commented at 7:18 am on May 6, 2022:
    I couldn’t make any sense of it, so reset it, but it is still here for anyone to look at: https://cirrus-ci.com/task/5612886578954240?logs=ci#L4869

    Crypt-iQ commented at 2:37 pm on May 6, 2022:

    I think this occurs and is unrelated to this PR because:

    0main thread:
    1* AppInitMain
    2  * ChainstateManager::ActiveTip()
    3    * CChain::Tip() -- accesses `vChain` without a lock (read)
    
    0b-loadblk thread:
    1* ThreadImport
    2  * ActivateBestChain called (src/node/blockstorage.cpp:883)
    3    * ActivateBestChainStep
    4      * ConnectTip
    5        * SetTip
    6          * vChain resize (write)
    
  29. maflcko approved
  30. dbwrapper: make CDBWrapper use new LevelDbOptions
    This change allows LevelDbOptions to destroy leveldb::Options
    members instead of doing so in CDBWrapper's destructor. This change
    prevents a LeakSanitizer exception if the CDBWrapper constructor
    throws.
    7177b4c04b
  31. dbwrapper: change CDBWrapper penv to unique_ptr, rename to m_env
    This changes the naming of penv to m_env and also makes it a
    unique_ptr. Like the previous commit, this prevents a LeakSanitizer
    exception if the constructor throws since the unique_ptr will clean
    itself up.
    b6a1677a55
  32. Crypt-iQ force-pushed on May 5, 2022
  33. achow101 commented at 6:36 pm on October 12, 2022: member
  34. DrahtBot commented at 4:08 pm on October 13, 2022: contributor

    🐙 This pull request conflicts with the target branch and needs rebase.

    Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a “draft”.

  35. DrahtBot added the label Needs rebase on Oct 13, 2022
  36. Crypt-iQ commented at 4:22 pm on October 18, 2022: contributor
    unfortunately don’t have time to work on this, lmk if i should close it
  37. fanquake added the label Up for grabs on Oct 19, 2022
  38. fanquake commented at 12:46 pm on October 19, 2022: member

    unfortunately don’t have time to work on this, lmk if i should close it

    Thanks. Closed and added up-for-grabs.

  39. fanquake closed this on Oct 19, 2022

  40. bitcoin locked this on Oct 19, 2023

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: 2024-12-21 15:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me