Skip to content

Conversation

@PierreRochard
Copy link
Contributor

If a user passes in a path with a trailing separator as the walletdir, multiple BerkeleyEnvironments may be created in the same directory which can lead to data corruption.

Discovered while reviewing #12493 (comment)

@fanquake fanquake added the Wallet label Sep 4, 2018
@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 4, 2018

Would it be better if the test mocked the g_dbenvs in db.cpp? I wasn't able to figure out how to do that.

@DrahtBot
Copy link
Contributor

DrahtBot commented Sep 4, 2018

Reviewers, this pull request conflicts with the following ones:

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.

@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 4, 2018

I'm unable to reproduce the CI failures locally for debugging and the logs don't seem to provide enough information to pinpoint the problem

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer the code down there. It might cause an infinity loop on Unix if the wallet filename is wallet\

while (fs::detail::is_directory_separator(env_directory.string().back())) {
    env_directory.remove_trailing_separator();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, that's much cleaner, fixed!

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

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

Move the path cleanup here?

path = gArgs.GetArg("-walletdir", "");

Copy link
Contributor

Choose a reason for hiding this comment

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

nit, remove space after ++i.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

Use boost::filesystem::path::preferred_separator?

Copy link
Member

Choose a reason for hiding this comment

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

@promag Not sure if you're aware, but anything boost::filesystem:: should use fs:: as is already being done here. See https://github.com/bitcoin/bitcoin/blob/master/src/fs.h.

Copy link
Contributor

@promag promag Sep 5, 2018

Choose a reason for hiding this comment

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

Yes, just wanted give full type for reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, that's great, fixed!

@ken2812221
Copy link
Contributor

ken2812221 commented Sep 5, 2018

I'm unable to reproduce the CI failures locally for debugging and the logs don't seem to provide enough information to pinpoint the problem

You can take a look at appveyor CI. That might be helpful.

get_fileid: file ID not set
Running 315 test cases...
unknown location(0): fatal error: in "psbt_wallet_tests/psbt_updater_test": class std::runtime_error: BerkeleyBatch: Can't open database wallet.dat (get_fileid failed with 22)
c:\projects\bitcoin\src\wallet\test\psbt_wallet_tests.cpp(18): last checkpoint: "psbt_updater_test" fixture ctor
get_fileid: file ID not set
unknown location(0): fatal error: in "psbt_wallet_tests/parse_hd_keypath": class std::runtime_error: BerkeleyBatch: Can't open database wallet.dat (get_fileid failed with 22)
c:\projects\bitcoin\src\wallet\test\psbt_wallet_tests.cpp(74): last checkpoint: "parse_hd_keypath" fixture ctor
get_fileid: file ID not set
unknown location(0): fatal error: in "wallet_tests/ComputeTimeSmart": class std::runtime_error: BerkeleyBatch: Can't open database wallet.dat (get_fileid failed with 22)
c:\projects\bitcoin\src\wallet\test\wallet_tests.cpp(230): last checkpoint: "ComputeTimeSmart" fixture ctor
get_fileid: file ID not set
unknown location(0): fatal error: in "wallet_tests/LoadReceiveRequests": class std::runtime_error: BerkeleyBatch: Can't open database wallet.dat (get_fileid failed with 22)
c:\projects\bitcoin\src\wallet\test\wallet_tests.cpp(256): last checkpoint: "LoadReceiveRequests" fixture ctor
get_fileid: file ID not set
unknown location(0): fatal error: in "wallet_tests/ListCoins": class std::runtime_error: BerkeleyBatch: Can't open database wallet.dat (get_fileid failed with 22)
c:\projects\bitcoin\src\wallet\test\wallet_tests.cpp(317): last checkpoint: "ListCoins" fixture ctor
*** 5 failures are detected in the test module "Bitcoin Test Suite"

@fanquake fanquake changed the title [wallet] Remove trailing separator for Berkeley db env directories wallet: Remove trailing separator for Berkeley db env directories Sep 6, 2018
@PierreRochard PierreRochard changed the title wallet: Remove trailing separator for Berkeley db env directories wallet: Remove trailing separators from -walletdir arg Sep 6, 2018
@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 6, 2018

@promag I moved the modification to walletutil.cpp, excellent suggestion, thank you!

Force push diff: PierreRochard@5a28a99


// Ensure that the directory does not end with a trailing separator to avoid
// creating two Berkeley environments in the same directory
while (fs::detail::is_directory_separator(path.string().back())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you could add above:

if (gArgs.IsArgSet("-walletdir")) {
    path = gArgs.GetArg("-walletdir", "");
    boost::system::error_code error;
    path = fs::canonical(path, error);
    if (error || !fs::is_directory(path)) {
        ...

fs::canonical checks if it exists and also cleans the path. For instance, locally calling canonical("/Users/promag/.///.//") gives /Users/promag.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah fs::canonical is even better! What do you think about me adding this to WalletInit::Verify here and then gArgs.ForceSetArg with the new clean path?
That way we keep the error checking in one place and the path gets cleaned up once, rather than every time GetWalletDir is called.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good, you could replace fs::exists with fs::canonical in

if (!fs::exists(wallet_dir)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The nuance with that approach is that we want to avoid transforming a relative file path to an absolute one, per @ryanofsky's commit message there:

Specifying paths relative to the current working directory in a daemon process can be dangerous, because files can fail to be located even if the configuration doesn't change, but the daemon is started up differently.

I'm thinking to first check wallet_dir.is_absolute() and then use fs::canonical

{
fs::path expected_wallet_dir = SetDataDir("get_wallet_dir");
std::string trailing_separators;
for (int i = 0; i < 4; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would add a couple of cases instead.

@PierreRochard
Copy link
Contributor Author

Took up @promag's excellent suggestion to clean up the path with fs::canonical. Added unit tests for the walletdir interaction in wallet init. If there are other (cross-platform) test cases you would like to see, I'm happy to add them. The commits have completely changed, no value in a force push diff.

#include <wallet/test/init_test_fixture.h>

#include <wallet/wallet.h>
#include <wallet/init.cpp>
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this include cpp file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The WalletInit class was moved from the header file to the cpp file in this pull request #12836

Should I move it back or is there a bigger picture architectural issue that I'm not seeing?

Copy link
Contributor

Choose a reason for hiding this comment

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

Include src/walletinitinterface.h?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤔 how would I create an instance of the WalletInit class to test its implementation of Verify?

(I'll take "read this book / article" as an answer if I'm out of my depth here haha)

Copy link
Contributor

Choose a reason for hiding this comment

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

The program will find the implementation by C++ virtual method table?

Copy link
Contributor

Choose a reason for hiding this comment

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

There is already an instance. Just replace the include.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah ok, fixed, I added init.h as well as it has the global instance in it

@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 11, 2018

Appveyor is failing, I rebooted into my Windows environment and see a memory leak is being detected, I'm debugging it now.

m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep;
m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep;


Copy link
Contributor

Choose a reason for hiding this comment

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

Remove 2nd empty line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

#include <wallet/test/init_test_fixture.h>

#include <wallet/wallet.h>
#include <wallet/init.cpp>
Copy link
Contributor

Choose a reason for hiding this comment

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

There is already an instance. Just replace the include.

@PierreRochard PierreRochard force-pushed the wallet-env-bug branch 2 times, most recently from 3f4d819 to 63fad10 Compare September 12, 2018 02:04
@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 12, 2018

I replaced the wallet/init.cpp include and removed extraneous whitespace, force push diff here c8b2076
I'm still working on the Windows memory leak issue.

@PierreRochard
Copy link
Contributor Author

PierreRochard commented Sep 13, 2018

I solved the problem on my local Windows machine, the fixture destructor was removing the current working directory - apparently this is ok on posix but not on Windows, force push diff: 6e2616d

@ken2812221
Copy link
Contributor

The unit tests is spamming error messages while running test_bitcoin. Would it confuse the user?

@PierreRochard
Copy link
Contributor Author

The unit tests is spamming error messages while running test_bitcoin. Would it confuse the user?

It definitely could, I've actually been researching how to properly mock CClientUIInterface. A shortcut would be to create a unit-test version of noui_connect but I was struggling on how to capture the output to check it in the unit test. If you have ideas I'm interested, whether they fit into the scope of this PR or not, as I'm sure this isn't the last time I'll be hitting InitMessage in a unit test.

@ken2812221
Copy link
Contributor

ken2812221 commented Sep 15, 2018

@PierreRochard Haven't tested, but can we use gArgs.SoftSetBoolArg("-printtoconsole", false);?

laanwj added a commit that referenced this pull request Oct 18, 2018
2d47163 wallet: Remove trailing separators from -walletdir arg (Pierre Rochard)
ea3009e wallet: Add walletdir arg unit tests (Pierre Rochard)

Pull request description:

  If a user passes in a path with a trailing separator as the `walletdir`, multiple BerkeleyEnvironments may be created in the same directory which can lead to data corruption.

  Discovered while reviewing #12493 (comment)

Tree-SHA512: f2bbf1749d904fd3f326b88f2ead58c8386034355910906d7faea155d518642e9cd4ceb3cae272f2d9d8feb61f126523e1c97502799d24e4315bb53e49fd7c09
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.

utACK 2d47163. This fix looks like it does the right thing, but it seems like it would be cleaner and more obvious to do the canonicalization in the GetWalletDir() function rather than WalletInit::Verify().

In particular I don't like how GetWalletDir() can return the wrong result if it is called before WalletInit::Verify() and the right result after. Ideally GetWalletDir() would just return the same, canonical result all the time, and I'm curious if canonicalizing in GetWalletDir() wouldn't work for some reason.

Another more minor issue predating this PR is that WalletInit::Verify() does the is_absolute check after checking whether the directory exists, and so might access the filesystem with a path relative to the working directory. Since bitcoind is a daemon, it's better if it can be agnostic to the working directory, and only access the filesystem with absolute paths (AbsPathForConfigVal util function is handy for this).

@PierreRochard PierreRochard deleted the wallet-env-bug branch October 20, 2018 02:14
maflcko pushed a commit to maflcko/bitcoin-core that referenced this pull request Nov 7, 2018
99d33a6 appveyor: Script improvement part II (Chun Kuan Lee)

Pull request description:

  - decrease clone depth to 5
  - Upgrade to python 3.7 that we can use `PYTHONUTF8` from PEP540.
  - Set clcache version to `v4.2.0`
  - Do not fetch the latest vcpkg package (The issue does not exist anymore)
  - Set test_bitcoin report sink and log sink to stdout and redirect stderr to NUL to drop confusing error messages that introduced by bitcoin#14146
  - discard vcpkg, bench_bitcoin output
  - Set functional test `--failfast` flag
  - Make the log be as clear as possible. (Only ~100 lines)

Tree-SHA512: e7e1f5c2698e8a5d15394edfb4b574508081e99ef4a353995f55657cb51e642567a128d6432a899ecae6f742494c143ac16e2e64df6c26e1e575421ee4a1df50
@bitcoin bitcoin deleted a comment May 4, 2019
fanquake added a commit to fanquake/bitcoin that referenced this pull request Oct 13, 2020
Due to the use of boost::filesystem::canonical(), the minimum required
version of Boost is actually 1.48.0. Use of canonical was introduced
in bitcoin#14146.

See also Boost filesystem 1.48.0 release notes:
https://github.com/boostorg/filesystem/blob/6b5e38134a336b6ea777cd8a69b6ae929819db7e/doc/release_history.html#L508
maflcko pushed a commit that referenced this pull request Oct 13, 2020
3562c15 build: set minimum required Boost to 1.48.0 (fanquake)

Pull request description:

  Due to the use of [`boost::filesystem::canonical()`](https://github.com/bitcoin/bitcoin/blob/80aa83aa406447d9b0932301b37966a30d0e1b6e/src/wallet/load.cpp#L21), the minimum required
  version of Boost is actually 1.48.0. Use of canonical was introduced
  in #14146.

  See also [Boost filesystem 1.48.0 release notes](https://github.com/boostorg/filesystem/blob/6b5e38134a336b6ea777cd8a69b6ae929819db7e/doc/release_history.html#L508). Also discussed in #20080.

ACKs for top commit:
  practicalswift:
    ACK 3562c15: correct is better than incorrect :)
  hebasto:
    ACK 3562c15, this is the status quo.

Tree-SHA512: 1d2226c60accb8e2276e023120a72f070392a6c1d3db97fb23e7759c174984226f81fed6d94f3203ef663fb4b3648e65960aaf15ed718895b6673e3ebeb082bd
fanquake added a commit to fanquake/bitcoin that referenced this pull request Oct 16, 2020
Due to the use of boost::filesystem::canonical(), the minimum required
version of Boost is actually 1.48.0. Use of canonical was introduced
in bitcoin#14146.

See also Boost filesystem 1.48.0 release notes:
https://github.com/boostorg/filesystem/blob/6b5e38134a336b6ea777cd8a69b6ae929819db7e/doc/release_history.html#L508

Github-Pull: bitcoin#20142
Rebased-From: 3562c15
Bushstar pushed a commit to Bushstar/omnicore that referenced this pull request Oct 21, 2020
Due to the use of boost::filesystem::canonical(), the minimum required
version of Boost is actually 1.48.0. Use of canonical was introduced
in bitcoin#14146.

See also Boost filesystem 1.48.0 release notes:
https://github.com/boostorg/filesystem/blob/6b5e38134a336b6ea777cd8a69b6ae929819db7e/doc/release_history.html#L508
linuxsh2 pushed a commit to linuxsh2/dash that referenced this pull request Sep 16, 2021
99d33a6 appveyor: Script improvement part II (Chun Kuan Lee)

Pull request description:

  - decrease clone depth to 5
  - Upgrade to python 3.7 that we can use `PYTHONUTF8` from PEP540.
  - Set clcache version to `v4.2.0`
  - Do not fetch the latest vcpkg package (The issue does not exist anymore)
  - Set test_bitcoin report sink and log sink to stdout and redirect stderr to NUL to drop confusing error messages that introduced by bitcoin#14146
  - discard vcpkg, bench_bitcoin output
  - Set functional test `--failfast` flag
  - Make the log be as clear as possible. (Only ~100 lines)

Tree-SHA512: e7e1f5c2698e8a5d15394edfb4b574508081e99ef4a353995f55657cb51e642567a128d6432a899ecae6f742494c143ac16e2e64df6c26e1e575421ee4a1df50
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Sep 18, 2021
…ir arg

2d47163 wallet: Remove trailing separators from -walletdir arg (Pierre Rochard)
ea3009e wallet: Add walletdir arg unit tests (Pierre Rochard)

Pull request description:

  If a user passes in a path with a trailing separator as the `walletdir`, multiple BerkeleyEnvironments may be created in the same directory which can lead to data corruption.

  Discovered while reviewing bitcoin#12493 (comment)

Tree-SHA512: f2bbf1749d904fd3f326b88f2ead58c8386034355910906d7faea155d518642e9cd4ceb3cae272f2d9d8feb61f126523e1c97502799d24e4315bb53e49fd7c09
kwvg pushed a commit to kwvg/dash that referenced this pull request Oct 16, 2021
…ir arg

2d47163 wallet: Remove trailing separators from -walletdir arg (Pierre Rochard)
ea3009e wallet: Add walletdir arg unit tests (Pierre Rochard)

Pull request description:

  If a user passes in a path with a trailing separator as the `walletdir`, multiple BerkeleyEnvironments may be created in the same directory which can lead to data corruption.

  Discovered while reviewing bitcoin#12493 (comment)

Tree-SHA512: f2bbf1749d904fd3f326b88f2ead58c8386034355910906d7faea155d518642e9cd4ceb3cae272f2d9d8feb61f126523e1c97502799d24e4315bb53e49fd7c09
pravblockc pushed a commit to pravblockc/dash that referenced this pull request Nov 18, 2021
…ir arg

2d47163 wallet: Remove trailing separators from -walletdir arg (Pierre Rochard)
ea3009e wallet: Add walletdir arg unit tests (Pierre Rochard)

Pull request description:

  If a user passes in a path with a trailing separator as the `walletdir`, multiple BerkeleyEnvironments may be created in the same directory which can lead to data corruption.

  Discovered while reviewing bitcoin#12493 (comment)

Tree-SHA512: f2bbf1749d904fd3f326b88f2ead58c8386034355910906d7faea155d518642e9cd4ceb3cae272f2d9d8feb61f126523e1c97502799d24e4315bb53e49fd7c09
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants