Skip to content

Commit c2d8ba6

Browse files
committed
Merge #19502: Bugfix: Wallet: Soft-fail exceptions within ListWalletDir file checks
24d2d33 QA: wallet_multiwallet: Check that recursive symlink directory and wallet.dat loops are ignored (Luke Dashjr) 69f59af Bugfix: Wallet: Soft-fail exceptions within ListWalletDir file checks (Luke Dashjr) Pull request description: Previously, an exception would be thrown, which could kill the node in some circumstances. Includes test changes to cause failure. Review with `?w=1` ACKs for top commit: hebasto: re-ACK 24d2d33, rebased only since my [previous](bitcoin/bitcoin#19502 (review)) review. promag: Tested ACK 24d2d33, test change fails on master. meshcollider: utACK 24d2d33 Tree-SHA512: f701f81b3aa3d3e15cee52ac9e7c31a73c0d8166e56bf077235294507cbcee099829fedc432a1c4b6d8780885f4e37897b44b980b08125771de3c849c000499e
2 parents d9f5132 + 24d2d33 commit c2d8ba6

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

src/wallet/walletutil.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,31 @@ std::vector<fs::path> ListWalletDir()
4949
continue;
5050
}
5151

52-
// Get wallet path relative to walletdir by removing walletdir from the wallet path.
53-
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
54-
const fs::path path = it->path().string().substr(offset);
52+
try {
53+
// Get wallet path relative to walletdir by removing walletdir from the wallet path.
54+
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
55+
const fs::path path = it->path().string().substr(offset);
5556

56-
if (it->status().type() == fs::directory_file &&
57-
(ExistsBerkeleyDatabase(it->path()) || ExistsSQLiteDatabase(it->path()))) {
58-
// Found a directory which contains wallet.dat btree file, add it as a wallet.
59-
paths.emplace_back(path);
60-
} else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) {
61-
if (it->path().filename() == "wallet.dat") {
62-
// Found top-level wallet.dat btree file, add top level directory ""
63-
// as a wallet.
64-
paths.emplace_back();
65-
} else {
66-
// Found top-level btree file not called wallet.dat. Current bitcoin
67-
// software will never create these files but will allow them to be
68-
// opened in a shared database environment for backwards compatibility.
69-
// Add it to the list of available wallets.
57+
if (it->status().type() == fs::directory_file &&
58+
(ExistsBerkeleyDatabase(it->path()) || ExistsSQLiteDatabase(it->path()))) {
59+
// Found a directory which contains wallet.dat btree file, add it as a wallet.
7060
paths.emplace_back(path);
61+
} else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) {
62+
if (it->path().filename() == "wallet.dat") {
63+
// Found top-level wallet.dat btree file, add top level directory ""
64+
// as a wallet.
65+
paths.emplace_back();
66+
} else {
67+
// Found top-level btree file not called wallet.dat. Current bitcoin
68+
// software will never create these files but will allow them to be
69+
// opened in a shared database environment for backwards compatibility.
70+
// Add it to the list of available wallets.
71+
paths.emplace_back(path);
72+
}
7173
}
74+
} catch (const std::exception& e) {
75+
LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what());
76+
it.no_push();
7277
}
7378
}
7479

test/functional/wallet_multiwallet.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from threading import Thread
1111
import os
1212
import shutil
13+
import stat
1314
import time
1415

1516
from test_framework.authproxy import JSONRPCException
@@ -78,6 +79,11 @@ def wallet_file(name):
7879
os.mkdir(wallet_dir('w7'))
7980
os.symlink('w7', wallet_dir('w7_symlink'))
8081

82+
os.symlink('..', wallet_dir('recursive_dir_symlink'))
83+
84+
os.mkdir(wallet_dir('self_walletdat_symlink'))
85+
os.symlink('wallet.dat', wallet_dir('self_walletdat_symlink/wallet.dat'))
86+
8187
# rename wallet.dat to make sure plain wallet file paths (as opposed to
8288
# directory paths) can be loaded
8389
# create another dummy wallet for use in testing backups later
@@ -117,7 +123,16 @@ def wallet_file(name):
117123
self.nodes[0].createwallet(wallet_name)
118124
for wallet_name in to_load:
119125
self.nodes[0].loadwallet(wallet_name)
120-
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), sorted(in_wallet_dir))
126+
127+
os.mkdir(wallet_dir('no_access'))
128+
os.chmod(wallet_dir('no_access'), 0)
129+
try:
130+
with self.nodes[0].assert_debug_log(expected_msgs=['Too many levels of symbolic links', 'Error scanning']):
131+
walletlist = self.nodes[0].listwalletdir()['wallets']
132+
finally:
133+
# Need to ensure access is restored for cleanup
134+
os.chmod(wallet_dir('no_access'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
135+
assert_equal(sorted(map(lambda w: w['name'], walletlist)), sorted(in_wallet_dir))
121136

122137
assert_equal(set(node.listwallets()), set(wallet_names))
123138

0 commit comments

Comments
 (0)