sqlite: prevent reentrant statement finalization · nodejs/node@c64b289 · GitHub
Skip to content

Commit c64b289

Browse files
committed
sqlite: prevent reentrant statement finalization
Track statement execution depth while calling sqlite3_step(). Reject close() and deserialize() while a statement is executing so callbacks cannot finalize the active statement and crash. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol
1 parent f00fb75 commit c64b289

4 files changed

Lines changed: 51 additions & 4 deletions

File tree

‎src/node_sqlite.cc‎

Lines changed: 18 additions & 4 deletions

‎src/node_sqlite.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,15 @@ class DatabaseSync : public BaseObject {
235235
private:
236236
bool Open();
237237
void DeleteSessions();
238+
int Step(sqlite3_stmt* statement);
238239

239240
~DatabaseSync() override;
240241
DatabaseOpenConfiguration open_config_;
241242
bool allow_load_extension_;
242243
bool enable_load_extension_;
243244
sqlite3* connection_;
244245
bool ignore_next_sqlite_error_;
246+
uint32_t statement_execution_depth_ = 0;
245247

246248
std::set<BackupJob*> backups_;
247249
std::unordered_set<Session*> sessions_;
@@ -251,6 +253,7 @@ class DatabaseSync : public BaseObject {
251253
friend class Session;
252254
friend class SQLTagStore;
253255
friend class StatementExecutionHelper;
256+
friend class StatementSyncIterator;
254257
};
255258

256259
class StatementSync : public BaseObject {

‎test/parallel/test-sqlite-custom-functions.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ const { DatabaseSync } = require('node:sqlite');
66
const { suite, test } = require('node:test');
77

88
suite('DatabaseSync.prototype.function()', () => {
9+
test('cannot close the database while a statement is executing', (t) => {
10+
const db = new DatabaseSync(':memory:');
11+
t.after(() => db.close());
12+
db.function('close_database', () => db.close());
13+
const stmt = db.prepare('SELECT close_database()');
14+
15+
t.assert.throws(() => stmt.get(), {
16+
code: 'ERR_INVALID_STATE',
17+
message: 'cannot close database while a statement is executing',
18+
});
19+
t.assert.strictEqual(db.isOpen, true);
20+
});
21+
922
suite('input validation', () => {
1023
const db = new DatabaseSync(':memory:');
1124

‎test/parallel/test-sqlite-serialize.js‎

Lines changed: 17 additions & 0 deletions

0 commit comments

Comments
 (0)