From 9f0f50a5a503895efb96a20545e4d06c7aaf0dce Mon Sep 17 00:00:00 2001 From: ndossche Date: Fri, 29 May 2026 12:06:49 +0200 Subject: [PATCH 1/2] sqlite: fix stack-use-after-scope with function callback The `hasIt` block has `Local`s, but it's capture in the lambda, yet the lambda is used after the locals go out of scope. Signed-off-by: ndossche --- src/node_sqlite.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 522d3c24cfba70..3e9894608b3791 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -2254,6 +2254,8 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { return; } + Local conflictFunc; + Local filterFunc; if (args.Length() > 1 && !args[1]->IsUndefined()) { if (!args[1]->IsObject()) { THROW_ERR_INVALID_ARG_TYPE(env->isolate(), @@ -2276,7 +2278,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { "The \"options.onConflict\" argument must be a function."); return; } - Local conflictFunc = conflictValue.As(); + conflictFunc = conflictValue.As(); context.conflictCallback = [env, conflictFunc](int conflictType) -> int { Local argv[] = {Integer::New(env->isolate(), conflictType)}; TryCatch try_catch(env->isolate()); @@ -2313,7 +2315,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { return; } - Local filterFunc = filterValue.As(); + filterFunc = filterValue.As(); context.filterCallback = [env, db, filterFunc](std::string_view item) -> bool { From 48fc045c329a227444eef18bf23aadb1016a1778 Mon Sep 17 00:00:00 2001 From: ndossche Date: Sun, 31 May 2026 10:36:11 +0200 Subject: [PATCH 2/2] Capture funcs by ref in lambda --- src/node_sqlite.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 3e9894608b3791..87cd52d7283f71 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -2279,7 +2279,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { return; } conflictFunc = conflictValue.As(); - context.conflictCallback = [env, conflictFunc](int conflictType) -> int { + context.conflictCallback = [env, &conflictFunc](int conflictType) -> int { Local argv[] = {Integer::New(env->isolate(), conflictType)}; TryCatch try_catch(env->isolate()); Local result = @@ -2318,7 +2318,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { filterFunc = filterValue.As(); context.filterCallback = - [env, db, filterFunc](std::string_view item) -> bool { + [env, db, &filterFunc](std::string_view item) -> bool { // If there was an error in the previous call to the filter's // callback, we skip calling it again. if (db->ignore_next_sqlite_error_) {