mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
sqlite: reject closing a session from a callback
SQLite runs "PRAGMA table_xinfo" from inside its pre-update hook, while it is still walking the connection's session list. Deleting a session from a callback that the PRAGMA triggers frees memory that walk is still using. Both an authorizer callback and a 'sqlite.db.query' subscriber reach that window, and either one segfaults. Reject session.close() and Symbol.dispose when the connection is inside any callback. An already-closed session stays a no-op so that disposal remains idempotent. Fixes: https://github.com/nodejs/node/issues/65428 Signed-off-by: Trevor Burnham <trevorburnham@gmail.com> PR-URL: https://github.com/nodejs/node/pull/65454 Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
1 parent
f7e2c140c8
commit
01c1300dfc
3 files changed
+174
-4
No files matched your search
@@ -169,6 +169,16 @@ inline MaybeLocal<Value> IntegerToValue(Isolate* isolate,
|
||||
sqlite3_stmt_busy((stmt)->statement_.get()), \
|
||||
"database cannot be accessed from an authorizer callback")
|
||||
|
||||
// SQLite's session module reaches back into JavaScript from inside the
|
||||
// pre-update hook, while it is still walking the connection's session list and
|
||||
// reading the table it found there. Deleting a session frees memory that walk
|
||||
// is still using, so no callback may close one.
|
||||
#define THROW_AND_RETURN_IF_SESSION_IN_CALLBACK(env, session) \
|
||||
THROW_AND_RETURN_ON_BAD_STATE( \
|
||||
(env), \
|
||||
(session)->database_->IsInCallback(), \
|
||||
"session cannot be closed while in a callback")
|
||||
|
||||
// A statement's virtual machine cannot be reentered while sqlite3_step() is
|
||||
// running it. Finalizing it frees the VM outright, and re-running it resets the
|
||||
// VM mid-execution; both are use-after-free rather than merely a contract
|
||||
@@ -4358,6 +4368,9 @@ void Session::Close(const FunctionCallbackInfo<Value>& args) {
|
||||
env, session->session_ == nullptr, "session is not open");
|
||||
THROW_AND_RETURN_ON_BAD_STATE(
|
||||
env, session->is_generating_changeset_, "session is currently in use");
|
||||
// Checked last: changeset generation runs the authorizer, so both conditions
|
||||
// hold in that case and the more specific message above has to win.
|
||||
THROW_AND_RETURN_IF_SESSION_IN_CALLBACK(env, session);
|
||||
|
||||
session->Delete();
|
||||
}
|
||||
@@ -4371,6 +4384,7 @@ void Session::Dispose(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
THROW_AND_RETURN_ON_BAD_STATE(
|
||||
env, session->is_generating_changeset_, "session is currently in use");
|
||||
THROW_AND_RETURN_IF_SESSION_IN_CALLBACK(env, session);
|
||||
|
||||
session->Delete();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user