test: add coverage for loading a wallet in a non-writable directory
What changed, and why it matters
This commit adds a new automated test to Bitcoin Core. The test checks that the program refuses to load a wallet stored in a directory where it cannot write files. The commit message says that, before this behavior existed, loading such a wallet would cause the node to crash whenever it later tried to write data. The change itself only adds a test; it does not change the wallet-loading code. So the actual crash-prevention logic must have been introduced in an earlier commit that is not shown here.
No immediate action is required for this commit because it only adds a test. Operators and reviewers should ensure the underlying wallet-loading guard that rejects non-writable directories is present in the release they run. If backporting or auditing older branches, verify that the corresponding product-code fix is included, since this test alone will not prevent crashes.
Security signals we found
Regression test for denial-of-service/crash scenario (wallet in non-writable directory)
Commit message explicitly states prior crash on subsequent write
No product code change in this commit; coverage only
Uses assert_raises_rpc_error to enforce safe failure mode
Evidence from the diff
The diff extends test/functional/wallet_startup.py with test_load_unwritable_wallet. It creates a wallet, unloads it, removes write permission from the wallet directory, and then expects loadwallet to fail with RPC error -4 and the message ‘SQLiteDatabase: Failed to open database in directory …: directory is not writable’. If permissions cannot be made read-only (e.g., running as root), the test skips the assertion. The test is purely additive (+28 lines) and contains no product-code changes, so it provides regression coverage for an already-implemented fix rather than implementing the fix itself.
Changed components
test/functional/wallet_startup.pyBitcoin Core wallet loading/SQLite database path validation (tested indirectly)Inspect captured patch +28 / −0
diff --git a/test/functional/wallet_startup.py b/test/functional/wallet_startup.py
index fe35ee12..2c5fb259 100755
--- a/test/functional/wallet_startup.py
+++ b/test/functional/wallet_startup.py
@@ -6,12 +6,17 @@
Verify that a bitcoind node can maintain list of wallets loading on startup
"""
+import os
import shutil
+import stat
import uuid
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_raises_rpc_error,
+ is_dir_writable,
)
@@ -38,6 +43,27 @@ class WalletStartupTest(BitcoinTestFramework):
shutil.move(self.nodes[0].wallets_path / wallet_name / "wallet.dat", self.nodes[0].wallets_path / "wallet.dat")
(self.nodes[0].wallets_path / wallet_name).rmdir()
+ def test_load_unwritable_wallet(self, node):
+ self.log.info("Test wallet load failure due to non-writable directory")
+ wallet_name = "bad_permissions"
+
+ node.createwallet(wallet_name)
+ node.unloadwallet(wallet_name)
+
+ dir_path = node.wallets_path / wallet_name
+ original_dir_perms = dir_path.stat().st_mode
+ os.chmod(dir_path, original_dir_perms & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH))
+
+ if is_dir_writable(dir_path):
+ self.log.warning("Skipping load non-writable directory test: unable to enforce read-only permissions")
+ else:
+ # Ensure we don't load a wallet located in a non-writable directory.
+ # The node will crash later on if we cannot write to disk.
+ assert_raises_rpc_error(-4, f"SQLiteDatabase: Failed to open database in directory '{str(dir_path)}': directory is not writable", node.loadwallet, wallet_name)
+
+ # Reset directory permissions for cleanup
+ dir_path.chmod(original_dir_perms)
+
def run_test(self):
self.log.info('Should start without any wallets')
assert_equal(self.nodes[0].listwallets(), [])
@@ -67,5 +93,7 @@ class WalletStartupTest(BitcoinTestFramework):
self.restart_node(0)
assert_equal(set(self.nodes[0].listwallets()), set(('w2', 'w3')))
+ self.test_load_unwritable_wallet(self.nodes[0])
+
if __name__ == '__main__':
WalletStartupTest(__file__).main()
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.