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

Commit c11abfd

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 6a3d80f commit c11abfd

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
@@ -234,13 +234,15 @@ class DatabaseSync : public BaseObject {
234234
private:
235235
bool Open();
236236
void DeleteSessions();
237+
int Step(sqlite3_stmt* statement);
237238

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

245247
std::set<BackupJob*> backups_;
246248
std::set<sqlite3_session*> sessions_;
@@ -250,6 +252,7 @@ class DatabaseSync : public BaseObject {
250252
friend class Session;
251253
friend class SQLTagStore;
252254
friend class StatementExecutionHelper;
255+
friend class StatementSyncIterator;
253256
};
254257

255258
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)