@@ -96,6 +96,27 @@ inline MaybeLocal<String> Utf8StringMaybeOneByte(Isolate* isolate,
9696 } \
9797 } while (0 )
9898
99+ // SQLite requires that an authorizer callback not modify the connection that
100+ // invoked it. Preparing and stepping statements both count as modifying it.
101+ // See https://www.sqlite.org/c3ref/set_authorizer.html.
102+ #define THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db ) \
103+ THROW_AND_RETURN_ON_BAD_STATE ( \
104+ (env), \
105+ (db)->IsInAuthorizerCallback (), \
106+ "database cannot be accessed from an authorizer callback")
107+
108+ // A statement's virtual machine cannot be reentered while sqlite3_step() is
109+ // running it. Finalizing it frees the VM outright, and re-running it resets the
110+ // VM mid-execution; both are use-after-free rather than merely a contract
111+ // violation. Callbacks that SQLite invokes during execution are therefore
112+ // barred from reaching the statement being stepped, though other statements on
113+ // the connection stay usable.
114+ #define THROW_AND_RETURN_IF_STEPPING (env, stmt ) \
115+ THROW_AND_RETURN_ON_BAD_STATE ( \
116+ (env), \
117+ (stmt)->db_->IsSteppingStatement ((stmt)->statement_), \
118+ "statement is already being executed")
119+
99120#define SQLITE_VALUE_TO_JS (from, isolate, use_big_int_args, result, ...) \
100121 do { \
101122 switch (sqlite3_##from##_type (__VA_ARGS__)) { \
@@ -825,6 +846,12 @@ Intercepted DatabaseSyncLimits::LimitsSetter(
825846 return Intercepted::kYes ;
826847 }
827848
849+ if (limits->database_ ->IsInAuthorizerCallback ()) {
850+ THROW_ERR_INVALID_STATE (
851+ env, " database cannot be accessed from an authorizer callback" );
852+ return Intercepted::kYes ;
853+ }
854+
828855 if (!value->IsNumber ()) {
829856 THROW_ERR_INVALID_ARG_TYPE (
830857 isolate, " Limit value must be a non-negative integer or Infinity." );
@@ -1081,6 +1108,7 @@ void DatabaseSync::CreateTagStore(const FunctionCallbackInfo<Value>& args) {
10811108 THROW_ERR_INVALID_STATE (env, " database is not open" );
10821109 return ;
10831110 }
1111+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
10841112 int capacity = 1000 ;
10851113 if (args.Length () > 0 && !args[0 ]->IsUndefined ()) {
10861114 if (!args[0 ]->IsNumber ()) {
@@ -1483,6 +1511,7 @@ void DatabaseSync::Prepare(const FunctionCallbackInfo<Value>& args) {
14831511 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
14841512 Environment* env = Environment::GetCurrent (args);
14851513 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1514+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
14861515
14871516 if (!args[0 ]->IsString ()) {
14881517 THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
@@ -1606,6 +1635,7 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
16061635 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
16071636 Environment* env = Environment::GetCurrent (args);
16081637 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1638+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
16091639
16101640 if (!args[0 ]->IsString ()) {
16111641 THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
@@ -1630,6 +1660,7 @@ void DatabaseSync::CustomFunction(const FunctionCallbackInfo<Value>& args) {
16301660 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
16311661 Environment* env = Environment::GetCurrent (args);
16321662 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1663+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
16331664
16341665 if (!args[0 ]->IsString ()) {
16351666 THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
@@ -1803,6 +1834,7 @@ void DatabaseSync::Serialize(const FunctionCallbackInfo<Value>& args) {
18031834 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
18041835 Environment* env = Environment::GetCurrent (args);
18051836 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1837+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
18061838
18071839 std::string db_name = " main" ;
18081840 if (!args[0 ]->IsUndefined ()) {
@@ -1937,6 +1969,7 @@ void DatabaseSync::AggregateFunction(const FunctionCallbackInfo<Value>& args) {
19371969 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
19381970 Environment* env = Environment::GetCurrent (args);
19391971 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1972+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
19401973 Utf8Value name (env->isolate (), args[0 ].As <String>());
19411974 Local<Object> options = args[1 ].As <Object>();
19421975 Local<Value> start_v;
@@ -2148,6 +2181,7 @@ void DatabaseSync::CreateSession(const FunctionCallbackInfo<Value>& args) {
21482181 DatabaseSync* db;
21492182 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
21502183 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
2184+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
21512185
21522186 sqlite3_session* pSession;
21532187 int r = sqlite3session_create (db->connection_ , db_name.c_str (), &pSession);
@@ -2317,6 +2351,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
23172351 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
23182352 Environment* env = Environment::GetCurrent (args);
23192353 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
2354+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
23202355
23212356 if (!args[0 ]->IsUint8Array ()) {
23222357 THROW_ERR_INVALID_ARG_TYPE (
@@ -2452,6 +2487,7 @@ void DatabaseSync::EnableLoadExtension(
24522487 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
24532488 Environment* env = Environment::GetCurrent (args);
24542489 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
2490+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
24552491
24562492 Isolate* isolate = env->isolate ();
24572493 if (!args[0 ]->IsBoolean ()) {
@@ -2480,6 +2516,7 @@ void DatabaseSync::EnableDefensive(const FunctionCallbackInfo<Value>& args) {
24802516 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
24812517 Environment* env = Environment::GetCurrent (args);
24822518 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
2519+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
24832520
24842521 Isolate* isolate = env->isolate ();
24852522 if (!args[0 ]->IsBoolean ()) {
@@ -2505,6 +2542,7 @@ void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) {
25052542 env, !db->allow_load_extension_ , " extension loading is not allowed" );
25062543 THROW_AND_RETURN_ON_BAD_STATE (
25072544 env, !db->enable_load_extension_ , " extension loading is not allowed" );
2545+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
25082546
25092547 if (!args[0 ]->IsString ()) {
25102548 THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
@@ -2533,6 +2571,7 @@ void DatabaseSync::SetAuthorizer(const FunctionCallbackInfo<Value>& args) {
25332571 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
25342572 Environment* env = Environment::GetCurrent (args);
25352573 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
2574+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, db);
25362575
25372576 Isolate* isolate = env->isolate ();
25382577
@@ -2569,6 +2608,7 @@ int DatabaseSync::AuthorizerCallback(void* user_data,
25692608 const char * param4) {
25702609 DatabaseSync* db = static_cast <DatabaseSync*>(user_data);
25712610 CallbackDepthGuard guard (db);
2611+ AuthorizerDepthGuard authorizer_guard (db);
25722612 Environment* env = db->env ();
25732613 Isolate* isolate = env->isolate ();
25742614 HandleScope handle_scope (isolate);
@@ -2682,12 +2722,20 @@ void StatementSync::Close(const FunctionCallbackInfo<Value>& args) {
26822722 Environment* env = Environment::GetCurrent (args);
26832723 THROW_AND_RETURN_ON_BAD_STATE (
26842724 env, stmt->IsFinalized (), " statement has been finalized" );
2725+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
26852726 stmt->Close ();
26862727}
26872728
26882729void StatementSync::Dispose (const FunctionCallbackInfo<Value>& args) {
26892730 StatementSync* stmt;
26902731 ASSIGN_OR_RETURN_UNWRAP (&stmt, args.This ());
2732+ Environment* env = Environment::GetCurrent (args);
2733+ // Disposal is idempotent, so an already-finalized statement is a no-op even
2734+ // inside a callback.
2735+ if (stmt->IsFinalized ()) {
2736+ return ;
2737+ }
2738+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
26912739 stmt->Close ();
26922740}
26932741
@@ -2968,6 +3016,7 @@ MaybeLocal<Value> StatementExecutionHelper::All(Environment* env,
29683016 LocalVector<Value> row_values (isolate);
29693017 LocalVector<Name> row_keys (isolate);
29703018
3019+ SteppingStatementGuard stepping (db, stmt);
29713020 while ((r = sqlite3_step (stmt)) == SQLITE_ROW ) {
29723021 if (num_cols == 0 ) {
29733022 num_cols = sqlite3_column_count (stmt);
@@ -3010,8 +3059,14 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
30103059 bool use_big_ints) {
30113060 Isolate* isolate = env->isolate ();
30123061 EscapableHandleScope scope (isolate);
3013- sqlite3_step (stmt);
3014- int r = sqlite3_reset (stmt);
3062+ int r;
3063+ {
3064+ // sqlite3_reset() can run JavaScript through an aggregate's xFinal, so it
3065+ // stays inside the guard.
3066+ SteppingStatementGuard stepping (db, stmt);
3067+ sqlite3_step (stmt);
3068+ r = sqlite3_reset (stmt);
3069+ }
30153070 CHECK_ERROR_OR_THROW (isolate, db, r, SQLITE_OK , MaybeLocal<Object>());
30163071
30173072 sqlite3_int64 last_insert_rowid = sqlite3_last_insert_rowid (db->Connection ());
@@ -3086,6 +3141,9 @@ MaybeLocal<Value> StatementExecutionHelper::Get(Environment* env,
30863141 bool use_big_ints) {
30873142 Isolate* isolate = env->isolate ();
30883143 EscapableHandleScope scope (isolate);
3144+ // Declared before the reset below so that it outlives it: sqlite3_reset()
3145+ // can run JavaScript through an aggregate's xFinal.
3146+ SteppingStatementGuard stepping (db, stmt);
30893147 auto reset = OnScopeLeave ([&]() { sqlite3_reset (stmt); });
30903148
30913149 int r = sqlite3_step (stmt);
@@ -3132,6 +3190,8 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
31323190 Environment* env = Environment::GetCurrent (args);
31333191 THROW_AND_RETURN_ON_BAD_STATE (
31343192 env, stmt->IsFinalized (), " statement has been finalized" );
3193+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, stmt->db_ .get ());
3194+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
31353195 Isolate* isolate = env->isolate ();
31363196 int r = stmt->ResetStatement ();
31373197 CHECK_ERROR_OR_THROW (isolate, stmt->db_ .get (), r, SQLITE_OK , void ());
@@ -3159,6 +3219,8 @@ void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
31593219 Environment* env = Environment::GetCurrent (args);
31603220 THROW_AND_RETURN_ON_BAD_STATE (
31613221 env, stmt->IsFinalized (), " statement has been finalized" );
3222+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, stmt->db_ .get ());
3223+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
31623224 int r = stmt->ResetStatement ();
31633225 CHECK_ERROR_OR_THROW (env->isolate (), stmt->db_ .get (), r, SQLITE_OK , void ());
31643226
@@ -3182,6 +3244,8 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
31823244 Environment* env = Environment::GetCurrent (args);
31833245 THROW_AND_RETURN_ON_BAD_STATE (
31843246 env, stmt->IsFinalized (), " statement has been finalized" );
3247+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, stmt->db_ .get ());
3248+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
31853249 int r = stmt->ResetStatement ();
31863250 CHECK_ERROR_OR_THROW (env->isolate (), stmt->db_ .get (), r, SQLITE_OK , void ());
31873251
@@ -3206,6 +3270,8 @@ void StatementSync::Run(const FunctionCallbackInfo<Value>& args) {
32063270 Environment* env = Environment::GetCurrent (args);
32073271 THROW_AND_RETURN_ON_BAD_STATE (
32083272 env, stmt->IsFinalized (), " statement has been finalized" );
3273+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, stmt->db_ .get ());
3274+ THROW_AND_RETURN_IF_STEPPING (env, stmt);
32093275 int r = stmt->ResetStatement ();
32103276 CHECK_ERROR_OR_THROW (env->isolate (), stmt->db_ .get (), r, SQLITE_OK , void ());
32113277
@@ -3488,13 +3554,16 @@ void SQLTagStore::Run(const FunctionCallbackInfo<Value>& args) {
34883554
34893555 THROW_AND_RETURN_ON_BAD_STATE (
34903556 env, !session->database_ ->IsOpen (), " database is not open" );
3557+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, session->database_ .get ());
34913558
34923559 BaseObjectPtr<StatementSync> stmt = PrepareStatement (args);
34933560
34943561 if (!stmt) {
34953562 return ;
34963563 }
34973564
3565+ THROW_AND_RETURN_IF_STEPPING (env, stmt.get ());
3566+
34983567 if (!ResetAndBindStatement (env, stmt.get (), args)) {
34993568 return ;
35003569 }
@@ -3514,13 +3583,16 @@ void SQLTagStore::Iterate(const FunctionCallbackInfo<Value>& args) {
35143583
35153584 THROW_AND_RETURN_ON_BAD_STATE (
35163585 env, !session->database_ ->IsOpen (), " database is not open" );
3586+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, session->database_ .get ());
35173587
35183588 BaseObjectPtr<StatementSync> stmt = PrepareStatement (args);
35193589
35203590 if (!stmt) {
35213591 return ;
35223592 }
35233593
3594+ THROW_AND_RETURN_IF_STEPPING (env, stmt.get ());
3595+
35243596 if (!ResetAndBindStatement (env, stmt.get (), args)) {
35253597 return ;
35263598 }
@@ -3542,13 +3614,16 @@ void SQLTagStore::Get(const FunctionCallbackInfo<Value>& args) {
35423614
35433615 THROW_AND_RETURN_ON_BAD_STATE (
35443616 env, !session->database_ ->IsOpen (), " database is not open" );
3617+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, session->database_ .get ());
35453618
35463619 BaseObjectPtr<StatementSync> stmt = PrepareStatement (args);
35473620
35483621 if (!stmt) {
35493622 return ;
35503623 }
35513624
3625+ THROW_AND_RETURN_IF_STEPPING (env, stmt.get ());
3626+
35523627 if (!ResetAndBindStatement (env, stmt.get (), args)) {
35533628 return ;
35543629 }
@@ -3571,13 +3646,16 @@ void SQLTagStore::All(const FunctionCallbackInfo<Value>& args) {
35713646
35723647 THROW_AND_RETURN_ON_BAD_STATE (
35733648 env, !session->database_ ->IsOpen (), " database is not open" );
3649+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, session->database_ .get ());
35743650
35753651 BaseObjectPtr<StatementSync> stmt = PrepareStatement (args);
35763652
35773653 if (!stmt) {
35783654 return ;
35793655 }
35803656
3657+ THROW_AND_RETURN_IF_STEPPING (env, stmt.get ());
3658+
35813659 if (!ResetAndBindStatement (env, stmt.get (), args)) {
35823660 return ;
35833661 }
@@ -3597,6 +3675,10 @@ void SQLTagStore::All(const FunctionCallbackInfo<Value>& args) {
35973675void SQLTagStore::Clear (const FunctionCallbackInfo<Value>& args) {
35983676 SQLTagStore* store;
35993677 ASSIGN_OR_RETURN_UNWRAP (&store, args.This ());
3678+ Environment* env = Environment::GetCurrent (args);
3679+ if (store->database_ ) {
3680+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, store->database_ .get ());
3681+ }
36003682 store->sql_tags_ .Clear ();
36013683}
36023684
@@ -3790,6 +3872,7 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
37903872 Environment* env = Environment::GetCurrent (args);
37913873 THROW_AND_RETURN_ON_BAD_STATE (
37923874 env, iter->stmt_ ->IsFinalized (), " statement has been finalized" );
3875+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, iter->stmt_ ->db_ .get ());
37933876 Isolate* isolate = env->isolate ();
37943877
37953878 auto iter_template = getLazyIterTemplate (env);
@@ -3812,6 +3895,10 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
38123895 iter->statement_reset_generation_ != iter->stmt_ ->reset_generation_ ,
38133896 " iterator was invalidated" );
38143897
3898+ // sqlite3_reset() can run JavaScript through an aggregate's xFinal, so it
3899+ // stays inside the guard.
3900+ SteppingStatementGuard stepping (iter->stmt_ ->db_ .get (),
3901+ iter->stmt_ ->statement_ );
38153902 int r = sqlite3_step (iter->stmt_ ->statement_ );
38163903 if (r != SQLITE_ROW ) {
38173904 CHECK_ERROR_OR_THROW (
@@ -3867,6 +3954,7 @@ void StatementSyncIterator::Return(const FunctionCallbackInfo<Value>& args) {
38673954 Environment* env = Environment::GetCurrent (args);
38683955 THROW_AND_RETURN_ON_BAD_STATE (
38693956 env, iter->stmt_ ->IsFinalized (), " statement has been finalized" );
3957+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, iter->stmt_ ->db_ .get ());
38703958 Isolate* isolate = env->isolate ();
38713959
38723960 sqlite3_reset (iter->stmt_ ->statement_ );
@@ -3945,6 +4033,7 @@ void Session::Changeset(const FunctionCallbackInfo<Value>& args) {
39454033 env, !session->database_ ->IsOpen (), " database is not open" );
39464034 THROW_AND_RETURN_ON_BAD_STATE (
39474035 env, session->session_ == nullptr , " session is not open" );
4036+ THROW_AND_RETURN_IF_IN_AUTHORIZER (env, session->database_ .get ());
39484037
39494038 int nChangeset;
39504039 void * pChangeset;
0 commit comments