db: support mode where we don't want to create the db.
What changed, and why it matters
This commit adds a new 'create' flag to Core Lightning's database-opening code. When create is false, the code tries to open an existing database but refuses to create a new one if it is missing, returning an error instead of crashing. This is a defensive change that makes the wallet startup behavior safer in some situations, but it does not by itself fix a known exploitable bug.
No immediate action required. Treat as a hardening/refactoring commit. If a future commit exposes the create=false mode to users, review that wiring for correct error handling and privilege assumptions.
Security signals we found
Adds defensive fail-closed mode for missing database
Removes unconditional database creation during open
Fixes sqlite3 connection leak on open error
No user-controlled create=false path introduced in this commit
Evidence from the diff
The patch changes db_open_() and the backend setup functions to accept a boolean create parameter. For SQLite, when create is false, SQLITE_OPEN_CREATE is omitted and a missing database returns false instead of calling db_fatal(). The db_setup() path used by normal lightningd startup still passes create=true, so production behavior is unchanged. The new mode is not yet wired to any user-facing option in this commit. The change also fixes a minor resource leak by closing the sqlite3 handle on open failure.
Changed components
db/common.hdb/db_postgres.cdb/db_sqlite3.cdb/utils.cdb/utils.hwallet/db.cwallet/test/run-db.cwallet/test/run-wallet.cInspect captured patch +27 / −15
diff --git a/db/common.h b/db/common.h
index 05436ef6..1ffd2634 100644
--- a/db/common.h
+++ b/db/common.h
@@ -183,7 +183,7 @@ struct db_config {
u64 (*last_insert_id_fn)(struct db_stmt *stmt);
size_t (*count_changes_fn)(struct db_stmt *stmt);
- bool (*setup_fn)(struct db *db);
+ bool (*setup_fn)(struct db *db, bool create);
void (*teardown_fn)(struct db *db);
bool (*vacuum_fn)(struct db *db);
diff --git a/db/db_postgres.c b/db/db_postgres.c
index 68cfd956..ae9785c6 100644
--- a/db/db_postgres.c
+++ b/db/db_postgres.c
@@ -14,7 +14,7 @@
#define INT4OID 23
#define TEXTOID 25
-static bool db_postgres_setup(struct db *db)
+static bool db_postgres_setup(struct db *db, bool create)
{
size_t prefix_len = strlen("postgres://");
diff --git a/db/db_sqlite3.c b/db/db_sqlite3.c
index 171c0e06..c34acc51 100644
--- a/db/db_sqlite3.c
+++ b/db/db_sqlite3.c
@@ -98,20 +98,23 @@ static const char *db_sqlite3_fmt_error(struct db_stmt *stmt)
sqlite3_errmsg(conn2sql(stmt->db->conn)));
}
-static bool db_sqlite3_setup(struct db *db)
+static bool db_sqlite3_setup(struct db *db, bool create)
{
char *filename;
char *sep;
char *backup_filename = NULL;
sqlite3_stmt *stmt;
sqlite3 *sql;
- int err, flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
-
+ int err, flags;
struct db_sqlite3 *wrapper;
if (!strstarts(db->filename, "sqlite3://") || strlen(db->filename) < 10)
db_fatal(db, "Could not parse the wallet DSN: %s", db->filename);
+ flags = SQLITE_OPEN_READWRITE;
+ if (create)
+ flags |= SQLITE_OPEN_CREATE;
+
/* Strip the scheme from the dsn. */
filename = db->filename + strlen("sqlite3://");
/* Look for a replica specification. */
@@ -126,8 +129,11 @@ static bool db_sqlite3_setup(struct db *db)
db->conn = wrapper;
err = sqlite3_open_v2(filename, &sql, flags, NULL);
-
if (err != SQLITE_OK) {
+ /* Note: even on error, the sql connection is allocated! */
+ sqlite3_close(sql);
+ if (!create)
+ return false;
db_fatal(db, "failed to open database %s: %s", filename,
sqlite3_errstr(err));
}
diff --git a/db/utils.c b/db/utils.c
index 70389907..155efbd4 100644
--- a/db/utils.c
+++ b/db/utils.c
@@ -333,6 +333,7 @@ void db_warn(const struct db *db, const char *fmt, ...)
struct db *db_open_(const tal_t *ctx, const char *filename,
bool developer,
+ bool create,
void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap),
void *arg)
{
@@ -356,7 +357,6 @@ struct db *db_open_(const tal_t *ctx, const char *filename,
if (!db->queries)
db_fatal(db, "Unable to find DB queries for %s", db->config->name);
- tal_add_destructor(db, destroy_db);
db->in_transaction = NULL;
db->changes = NULL;
@@ -365,10 +365,15 @@ struct db *db_open_(const tal_t *ctx, const char *filename,
trace_span_start("db_setup", db);
db_prepare_for_changes(db);
- if (db->config->setup_fn && !db->config->setup_fn(db))
- db_fatal(db, "Error calling DB setup: %s", db->error);
+ if (!db->config->setup_fn(db, create)) {
+ if (create)
+ db_fatal(db, "Error calling DB setup: %s", db->error);
+ trace_span_end(db);
+ return tal_free(db);
+ }
db_report_changes(db, NULL, 0);
trace_span_end(db);
+ tal_add_destructor(db, destroy_db);
return db;
}
diff --git a/db/utils.h b/db/utils.h
index 0cb699ee..859741f8 100644
--- a/db/utils.h
+++ b/db/utils.h
@@ -83,15 +83,16 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db,
const char *query_id);
/**
- * db_open - Open or create a database
+ * db_open - Open or create (if create set) a database
*/
-#define db_open(ctx, filename, developer, errfn, arg) \
- db_open_((ctx), (filename), (developer), \
+#define db_open(ctx, filename, developer, create, errfn, arg) \
+ db_open_((ctx), (filename), (developer), (create), \
typesafe_cb_postargs(void, void *, (errfn), (arg), \
bool, const char *, va_list), \
(arg))
struct db *db_open_(const tal_t *ctx, const char *filename, bool developer,
+ bool create,
void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap),
void *arg);
diff --git a/wallet/db.c b/wallet/db.c
index c97d2960..faae6161 100644
--- a/wallet/db.c
+++ b/wallet/db.c
@@ -1171,7 +1171,7 @@ static void db_error(struct lightningd *ld, bool fatal, const char *fmt, va_list
struct db *db_setup(const tal_t *ctx, struct lightningd *ld,
const struct ext_key *bip32_base)
{
- struct db *db = db_open(ctx, ld->wallet_dsn, ld->developer,
+ struct db *db = db_open(ctx, ld->wallet_dsn, ld->developer, true,
db_error, ld);
bool migrated;
diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c
index d9a99d8f..a010a1b0 100644
--- a/wallet/test/run-db.c
+++ b/wallet/test/run-db.c
@@ -427,7 +427,7 @@ static struct db *create_test_db(void)
dsn = tal_fmt(NULL, "sqlite3://%s", filename);
tal_free(filename);
- db = db_open(NULL, dsn, true, db_error, (struct lightningd *)NULL);
+ db = db_open(NULL, dsn, true, true, db_error, (struct lightningd *)NULL);
db->data_version = 0;
db->report_changes_fn = NULL;
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 2caa7d9e..31c8f91b 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -1348,7 +1348,7 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx
close(fd);
dsn = tal_fmt(NULL, "sqlite3://%s", filename);
- w->db = db_open(w, dsn, true, test_error, ld);
+ w->db = db_open(w, dsn, true, true, test_error, ld);
w->db->report_changes_fn = NULL;
tal_free(dsn);
tal_add_destructor2(w, cleanup_test_wallet, filename);
Why this scored 20/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.