Skip to content

Commit 870f499

Browse files
mcollinaaduh95
authored andcommitted
sqlite: fix use-after-free in Exec() and ApplyChangeset()
When sqlite3_exec() or sqlite3changeset_apply() call JavaScript callbacks (user-defined functions, conflict handlers, or filter callbacks), the DatabaseSync object could be garbage-collected if the JavaScript code drops all references to it. Both methods only held a raw DatabaseSync* pointer on the C++ stack, which V8 GC does not track. Add a BaseObjectPtr<DatabaseSync> guard that keeps the database alive for the duration of these SQLite API calls, preventing a use-after-free when the JavaScript callback triggers GC. Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: #64535 Reviewed-By: Edy Silva <edigleyssonsilva@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
1 parent ca60942 commit 870f499

1 file changed

Lines changed: 14 additions & 0 deletions

File tree

src/node_sqlite.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,13 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
15841584
return;
15851585
}
15861586

1587+
// Keep the database alive during sqlite3_exec(), which may call
1588+
// user-defined SQLite functions that trigger JavaScript callbacks.
1589+
// If the JavaScript callback drops all references to the database,
1590+
// the DatabaseSync could otherwise be garbage-collected while the
1591+
// SQLite callback is still executing, causing a use-after-free.
1592+
BaseObjectPtr<DatabaseSync> guard(db);
1593+
15871594
Utf8Value sql(env->isolate(), args[0].As<String>());
15881595
int r = sqlite3_exec(db->connection_, *sql, nullptr, nullptr, nullptr);
15891596
CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void());
@@ -2358,6 +2365,13 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
23582365
}
23592366
}
23602367

2368+
// Keep the database alive during sqlite3changeset_apply(), which may
2369+
// call conflict or filter callbacks that trigger JavaScript execution.
2370+
// If the JavaScript callback drops all references to the database,
2371+
// the DatabaseSync could otherwise be garbage-collected while the
2372+
// callback is still executing, causing a use-after-free.
2373+
BaseObjectPtr<DatabaseSync> guard(db);
2374+
23612375
ArrayBufferViewContents<uint8_t> buf(args[0]);
23622376
int r = sqlite3changeset_apply(
23632377
db->connection_,

0 commit comments

Comments
 (0)