Skip to content
Bitcoin Roots

v29.4-roots.3 · Commit 4: [PATCH 04/30] wallet: harden database and signing behavior

src/wallet/sqlite.cpp

Before: src/wallet/sqlite.cpp · After: src/wallet/sqlite.cpp

File metadata
index 896a2fc0f33fdf8e93ec97b6b2f723322b28f973..34429fb34b471e3ce9035223d5741141912c36ce 100644

Download original patch · Release notes

Diff rows 1–156 of 156. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/wallet/sqlite.cpp CHANGED
@@ -40,7 +40,7 @@ static void ErrorLogCallback(void* arg, int code, const char* msg)
40
  // invoked."
41
  // Assert that this is the case:
42
  assert(arg == nullptr);
43
- LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
44
  }
45
 
46
  static int TraceSqlCallback(unsigned code, void* context, void* param1, void* param2)
@@ -69,7 +69,7 @@ static bool BindBlobToStatement(sqlite3_stmt* stmt,
69
  // instead of the empty blob value X'', which would mess up SQL comparisons.
70
  int res = sqlite3_bind_blob(stmt, index, blob.data() ? static_cast<const void*>(blob.data()) : "", blob.size(), SQLITE_STATIC);
71
  if (res != SQLITE_OK) {
72
- LogPrintf("Unable to bind %s to statement: %s\n", description, sqlite3_errstr(res));
73
  sqlite3_clear_bindings(stmt);
74
  sqlite3_reset(stmt);
75
  return false;
@@ -182,7 +182,7 @@ void SQLiteDatabase::Cleanup() noexcept
182
  if (--g_sqlite_count == 0) {
183
  int ret = sqlite3_shutdown();
184
  if (ret != SQLITE_OK) {
185
- LogPrintf("SQLiteDatabase: Failed to shutdown SQLite: %s\n", sqlite3_errstr(ret));
186
  }
187
  }
188
  }
@@ -254,7 +254,11 @@ void SQLiteDatabase::Open()
254
  if (m_db == nullptr) {
255
  if (!m_mock) {
256
  TryCreateDirectories(m_dir_path);
 
 
 
257
  }
 
258
  int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
259
  if (ret != SQLITE_OK) {
260
  throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
@@ -267,7 +271,7 @@ void SQLiteDatabase::Open()
267
  if (LogAcceptCategory(BCLog::WALLETDB, BCLog::Level::Trace)) {
268
  ret = sqlite3_trace_v2(m_db, SQLITE_TRACE_STMT, TraceSqlCallback, this);
269
  if (ret != SQLITE_OK) {
270
- LogPrintf("Failed to enable SQL tracing for %s\n", Filename());
271
  }
272
  }
273
  }
@@ -294,7 +298,7 @@ void SQLiteDatabase::Open()
294
 
295
  if (m_use_unsafe_sync) {
296
  // Use normal synchronous mode for the journal
297
- LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n");
298
  SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
299
  }
300
 
@@ -353,14 +357,14 @@ bool SQLiteDatabase::Backup(const std::string& dest) const
353
  }
354
  sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
355
  if (!backup) {
356
- LogPrintf("%s: Unable to begin backup: %s\n", __func__, sqlite3_errmsg(m_db));
357
  sqlite3_close(db_copy);
358
  return false;
359
  }
360
  // Specifying -1 will copy all of the pages
361
  res = sqlite3_backup_step(backup, -1);
362
  if (res != SQLITE_DONE) {
363
- LogPrintf("%s: Unable to backup: %s\n", __func__, sqlite3_errstr(res));
364
  sqlite3_backup_finish(backup);
365
  sqlite3_close(db_copy);
366
  return false;
@@ -412,13 +416,13 @@ void SQLiteBatch::Close()
412
  // If we began a transaction, and it wasn't committed, abort the transaction in progress
413
  if (m_txn) {
414
  if (TxnAbort()) {
415
- LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");
416
  } else {
417
  // If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case
418
  // by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction
419
  // was opened will be rolled back and future transactions can succeed without committing old data.
420
  force_conn_refresh = true;
421
- LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..\n");
422
  }
423
  }
424
 
@@ -434,7 +438,7 @@ void SQLiteBatch::Close()
434
  for (const auto& [stmt_prepared, stmt_description] : statements) {
435
  int res = sqlite3_finalize(*stmt_prepared);
436
  if (res != SQLITE_OK) {
437
- LogPrintf("SQLiteBatch: Batch closed but could not finalize %s statement: %s\n",
438
  stmt_description, sqlite3_errstr(res));
439
  }
440
  *stmt_prepared = nullptr;
@@ -465,7 +469,7 @@ bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value)
465
  if (res != SQLITE_ROW) {
466
  if (res != SQLITE_DONE) {
467
  // SQLITE_DONE means "not found", don't log an error in that case.
468
- LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
469
  }
470
  sqlite3_clear_bindings(m_read_stmt);
471
  sqlite3_reset(m_read_stmt);
@@ -505,7 +509,7 @@ bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
505
  sqlite3_clear_bindings(stmt);
506
  sqlite3_reset(stmt);
507
  if (res != SQLITE_DONE) {
508
- LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
509
  }
510
 
511
  if (!m_txn) m_database.m_write_semaphore.post();
@@ -529,7 +533,7 @@ bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, Span<const std::byte> blob)
529
  sqlite3_clear_bindings(stmt);
530
  sqlite3_reset(stmt);
531
  if (res != SQLITE_DONE) {
532
- LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
533
  }
534
 
535
  if (!m_txn) m_database.m_write_semaphore.post();
@@ -567,7 +571,7 @@ DatabaseCursor::Status SQLiteCursor::Next(DataStream& key, DataStream& value)
567
  return Status::DONE;
568
  }
569
  if (res != SQLITE_ROW) {
570
- LogPrintf("%s: Unable to execute cursor step: %s\n", __func__, sqlite3_errstr(res));
571
  return Status::FAIL;
572
  }
573
 
@@ -586,8 +590,8 @@ SQLiteCursor::~SQLiteCursor()
586
  sqlite3_reset(m_cursor_stmt);
587
  int res = sqlite3_finalize(m_cursor_stmt);
588
  if (res != SQLITE_OK) {
589
- LogPrintf("%s: cursor closed but could not finalize cursor statement: %s\n",
590
- __func__, sqlite3_errstr(res));
591
  }
592
  }
593
 
@@ -655,7 +659,7 @@ bool SQLiteBatch::TxnBegin()
655
  Assert(!m_database.HasActiveTxn());
656
  int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION");
657
  if (res != SQLITE_OK) {
658
- LogPrintf("SQLiteBatch: Failed to begin the transaction\n");
659
  m_database.m_write_semaphore.post();
660
  } else {
661
  m_txn = true;
@@ -669,7 +673,7 @@ bool SQLiteBatch::TxnCommit()
669
  Assert(m_database.HasActiveTxn());
670
  int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION");
671
  if (res != SQLITE_OK) {
672
- LogPrintf("SQLiteBatch: Failed to commit the transaction\n");
673
  } else {
674
  m_txn = false;
675
  m_database.m_write_semaphore.post();
@@ -683,7 +687,7 @@ bool SQLiteBatch::TxnAbort()
683
  Assert(m_database.HasActiveTxn());
684
  int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION");
685
  if (res != SQLITE_OK) {
686
- LogPrintf("SQLiteBatch: Failed to abort the transaction\n");
687
  } else {
688
  m_txn = false;
689
  m_database.m_write_semaphore.post();
 
40
  // invoked."
41
  // Assert that this is the case:
42
  assert(arg == nullptr);
43
+ LogWarning("SQLite Error. Code: %d. Message: %s", code, msg);
44
  }
45
 
46
  static int TraceSqlCallback(unsigned code, void* context, void* param1, void* param2)
 
69
  // instead of the empty blob value X'', which would mess up SQL comparisons.
70
  int res = sqlite3_bind_blob(stmt, index, blob.data() ? static_cast<const void*>(blob.data()) : "", blob.size(), SQLITE_STATIC);
71
  if (res != SQLITE_OK) {
72
+ LogWarning("Unable to bind %s to statement: %s", description, sqlite3_errstr(res));
73
  sqlite3_clear_bindings(stmt);
74
  sqlite3_reset(stmt);
75
  return false;
 
182
  if (--g_sqlite_count == 0) {
183
  int ret = sqlite3_shutdown();
184
  if (ret != SQLITE_OK) {
185
+ LogWarning("SQLiteDatabase: Failed to shutdown SQLite: %s", sqlite3_errstr(ret));
186
  }
187
  }
188
  }
 
254
  if (m_db == nullptr) {
255
  if (!m_mock) {
256
  TryCreateDirectories(m_dir_path);
257
+ if (!IsDirWritable(m_dir_path)) {
258
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database in directory '%s': directory is not writable", fs::PathToString(m_dir_path)));
259
+ }
260
  }
261
+
262
  int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
263
  if (ret != SQLITE_OK) {
264
  throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
 
271
  if (LogAcceptCategory(BCLog::WALLETDB, BCLog::Level::Trace)) {
272
  ret = sqlite3_trace_v2(m_db, SQLITE_TRACE_STMT, TraceSqlCallback, this);
273
  if (ret != SQLITE_OK) {
274
+ LogWarning("Failed to enable SQL tracing for %s", Filename());
275
  }
276
  }
277
  }
 
298
 
299
  if (m_use_unsafe_sync) {
300
  // Use normal synchronous mode for the journal
301
+ LogWarning("SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.");
302
  SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
303
  }
304
 
 
357
  }
358
  sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
359
  if (!backup) {
360
+ LogWarning("Unable to begin sqlite backup: %s", sqlite3_errmsg(m_db));
361
  sqlite3_close(db_copy);
362
  return false;
363
  }
364
  // Specifying -1 will copy all of the pages
365
  res = sqlite3_backup_step(backup, -1);
366
  if (res != SQLITE_DONE) {
367
+ LogWarning("Unable to continue sqlite backup: %s", sqlite3_errstr(res));
368
  sqlite3_backup_finish(backup);
369
  sqlite3_close(db_copy);
370
  return false;
 
416
  // If we began a transaction, and it wasn't committed, abort the transaction in progress
417
  if (m_txn) {
418
  if (TxnAbort()) {
419
+ LogWarning("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted");
420
  } else {
421
  // If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case
422
  // by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction
423
  // was opened will be rolled back and future transactions can succeed without committing old data.
424
  force_conn_refresh = true;
425
+ LogWarning("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..");
426
  }
427
  }
428
 
 
438
  for (const auto& [stmt_prepared, stmt_description] : statements) {
439
  int res = sqlite3_finalize(*stmt_prepared);
440
  if (res != SQLITE_OK) {
441
+ LogWarning("SQLiteBatch: Batch closed but could not finalize %s statement: %s",
442
  stmt_description, sqlite3_errstr(res));
443
  }
444
  *stmt_prepared = nullptr;
 
469
  if (res != SQLITE_ROW) {
470
  if (res != SQLITE_DONE) {
471
  // SQLITE_DONE means "not found", don't log an error in that case.
472
+ LogWarning("Unable to execute read statement: %s", sqlite3_errstr(res));
473
  }
474
  sqlite3_clear_bindings(m_read_stmt);
475
  sqlite3_reset(m_read_stmt);
 
509
  sqlite3_clear_bindings(stmt);
510
  sqlite3_reset(stmt);
511
  if (res != SQLITE_DONE) {
512
+ LogWarning("Unable to execute write statement: %s", sqlite3_errstr(res));
513
  }
514
 
515
  if (!m_txn) m_database.m_write_semaphore.post();
 
533
  sqlite3_clear_bindings(stmt);
534
  sqlite3_reset(stmt);
535
  if (res != SQLITE_DONE) {
536
+ LogWarning("Unable to execute exec statement: %s", sqlite3_errstr(res));
537
  }
538
 
539
  if (!m_txn) m_database.m_write_semaphore.post();
 
571
  return Status::DONE;
572
  }
573
  if (res != SQLITE_ROW) {
574
+ LogWarning("Unable to execute cursor step: %s", sqlite3_errstr(res));
575
  return Status::FAIL;
576
  }
577
 
 
590
  sqlite3_reset(m_cursor_stmt);
591
  int res = sqlite3_finalize(m_cursor_stmt);
592
  if (res != SQLITE_OK) {
593
+ LogWarning("Cursor closed but could not finalize cursor statement: %s",
594
+ sqlite3_errstr(res));
595
  }
596
  }
597
 
 
659
  Assert(!m_database.HasActiveTxn());
660
  int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION");
661
  if (res != SQLITE_OK) {
662
+ LogWarning("SQLiteBatch: Failed to begin the transaction");
663
  m_database.m_write_semaphore.post();
664
  } else {
665
  m_txn = true;
 
673
  Assert(m_database.HasActiveTxn());
674
  int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION");
675
  if (res != SQLITE_OK) {
676
+ LogWarning("SQLiteBatch: Failed to commit the transaction");
677
  } else {
678
  m_txn = false;
679
  m_database.m_write_semaphore.post();
 
687
  Assert(m_database.HasActiveTxn());
688
  int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION");
689
  if (res != SQLITE_OK) {
690
+ LogWarning("SQLiteBatch: Failed to abort the transaction");
691
  } else {
692
  m_txn = false;
693
  m_database.m_write_semaphore.post();