upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-29 17:41:10 -0300
committerGitHub <noreply@github.com>2026-07-29 17:41:10 -0300
commit72683da2b01286030fd61228168712c84aea79d4 (patch)
tree0b299e830282e483a1642e3b2ca38fb68507e646 /src/db.cpp
parentc404e1c7f5d3f04d07e7e5b35a7a8ffcc998d855 (diff)
db: catch BDB exceptions so unreadable files fail cleanly (#34) (#38)
Berkeley DB throws when it cannot open a file written by another platform's build (e.g. BDB 5.3 wallet read by 6.2). Nothing on the call path caught these, so the exception unwound into std::terminate -- surfacing on Windows as STATUS_STACK_BUFFER_OVERRUN (0xC0000409) with no useful message. Three layers of defense: 1. main_gui.cpp: try/catch around LoadBlockIndex() and LoadWallet(), printing the reason and exiting cleanly. 2. db.cpp LoadWallet: try/catch around the record-reading loop (Satoshi's 2009 TODO), tracking the last record type so the error message says what it choked on. 3. db.cpp cleanup paths: every dbenv.close(), pdb->close(), txn_checkpoint() and log_archive() wrapped in try/catch so the environment teardown after a failed open does not throw a second exception into std::terminate. Tested: cross-platform wallet (Linux BDB 5.3 → Windows 6.2) now prints a clear message and exits with code 1, wallet left untouched. Closes #34 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp47
1 files changed, 41 insertions, 6 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 199fa44..d43e4ef 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -28,7 +28,8 @@ public:
28 { 28 {
29 if (fDbEnvInit) 29 if (fDbEnvInit)
30 { 30 {
31 dbenv.close(0); 31 try { dbenv.close(0); }
32 catch (...) { }
32 fDbEnvInit = false; 33 fDbEnvInit = false;
33 } 34 }
34 } 35 }
@@ -118,10 +119,10 @@ void CDB::Close()
118 if (!vTxn.empty()) 119 if (!vTxn.empty())
119 vTxn.front()->abort(); 120 vTxn.front()->abort();
120 vTxn.clear(); 121 vTxn.clear();
121 pdb->close(0); 122 try { pdb->close(0); } catch (...) { }
122 delete pdb; 123 delete pdb;
123 pdb = NULL; 124 pdb = NULL;
124 dbenv.txn_checkpoint(0, 0, 0); 125 try { dbenv.txn_checkpoint(0, 0, 0); } catch (...) { }
125 126
126 CRITICAL_BLOCK(cs_db) 127 CRITICAL_BLOCK(cs_db)
127 --mapFileUseCount[strFile]; 128 --mapFileUseCount[strFile];
@@ -154,8 +155,8 @@ void DBFlush(bool fShutdown)
154 { 155 {
155 char** listp; 156 char** listp;
156 if (mapFileUseCount.empty()) 157 if (mapFileUseCount.empty())
157 dbenv.log_archive(&listp, DB_ARCH_REMOVE); 158 try { dbenv.log_archive(&listp, DB_ARCH_REMOVE); } catch (...) { }
158 dbenv.close(0); 159 try { dbenv.close(0); } catch (...) { }
159 fDbEnvInit = false; 160 fDbEnvInit = false;
160 } 161 }
161 } 162 }
@@ -427,7 +428,25 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
427{ 428{
428 vchDefaultKeyRet.clear(); 429 vchDefaultKeyRet.clear();
429 430
430 //// todo: shouldn't we catch exceptions and try to recover and continue? 431 // Satoshi's "todo: shouldn't we catch exceptions" sat here since 2009, and
432 // it was not a nicety. Nothing on this path caught anything, so a record
433 // this build could not read -- a wallet from another platform's Berkeley
434 // DB, a truncated file, a field it does not understand -- threw out of
435 // LoadWallet, out of main(), into std::terminate and abort(). On Windows
436 // that surfaces as STATUS_STACK_BUFFER_OVERRUN (0xC0000409) in
437 // ucrtbase.dll: no message, no log line past "Loading wallet...", and a
438 // faulting module that has nothing to do with the actual problem.
439 //
440 // The program knew exactly what had gone wrong and threw the reason away.
441 // Now it says so and stops cleanly, which is the difference between "your
442 // wallet is unreadable and here is why" and a crash nobody can act on.
443 //
444 // Deliberately a hard failure rather than skipping the bad record: a
445 // partially loaded wallet is worse than one that refuses to open, because
446 // the balance looks plausible while keys or transactions are missing.
447 string strLastType;
448 try
449 {
431 CRITICAL_BLOCK(cs_mapKeys) 450 CRITICAL_BLOCK(cs_mapKeys)
432 CRITICAL_BLOCK(cs_mapWallet) 451 CRITICAL_BLOCK(cs_mapWallet)
433 { 452 {
@@ -452,6 +471,7 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
452 // is just the two items serialized one after the other 471 // is just the two items serialized one after the other
453 string strType; 472 string strType;
454 ssKey >> strType; 473 ssKey >> strType;
474 strLastType = strType;
455 if (strType == "name") 475 if (strType == "name")
456 { 476 {
457 string strAddress; 477 string strAddress;
@@ -509,6 +529,21 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
509 } 529 }
510 } 530 }
511 } 531 }
532 }
533 catch (const std::exception& e)
534 {
535 printf("LoadWallet: wallet.dat could not be read. Failed while handling "
536 "a '%s' record: %s\n", strLastType.c_str(), e.what());
537 printf("LoadWallet: the file was left untouched. A wallet.dat written by "
538 "a different platform's Berkeley DB is the usual cause.\n");
539 return false;
540 }
541 catch (...)
542 {
543 printf("LoadWallet: wallet.dat could not be read. Unknown failure while "
544 "handling a '%s' record.\n", strLastType.c_str());
545 return false;
546 }
512 547
513 printf("nTransactionFee = %lld\n", nTransactionFee); 548 printf("nTransactionFee = %lld\n", nTransactionFee);
514 printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); 549 printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());