sqlite: fix crash session extension callbacks with workers · nodejs/node@3eeb7b4

@@ -1668,26 +1668,28 @@ void Backup(const FunctionCallbackInfo<Value>& args) {

16681668

job->ScheduleBackup();

16691669

}

167016701671+

struct ConflictCallbackContext {

1672+

std::function<bool(std::string_view)> filterCallback;

1673+

std::function<int(int)> conflictCallback;

1674+

};

1675+16711676

// the reason for using static functions here is that SQLite needs a

16721677

// function pointer

1673-

static std::function<int(int)> conflictCallback;

1674167816751679

static int xConflict(void* pCtx, int eConflict, sqlite3_changeset_iter* pIter) {

1676-

if (!conflictCallback) return SQLITE_CHANGESET_ABORT;

1677-

return conflictCallback(eConflict);

1680+

auto ctx = static_cast<ConflictCallbackContext*>(pCtx);

1681+

if (!ctx->conflictCallback) return SQLITE_CHANGESET_ABORT;

1682+

return ctx->conflictCallback(eConflict);

16781683

}

167916841680-

static std::function<bool(std::string)> filterCallback;

1681-16821685

static int xFilter(void* pCtx, const char* zTab) {

1683-

if (!filterCallback) return 1;

1684-1685-

return filterCallback(zTab) ? 1 : 0;

1686+

auto ctx = static_cast<ConflictCallbackContext*>(pCtx);

1687+

if (!ctx->filterCallback) return 1;

1688+

return ctx->filterCallback(zTab) ? 1 : 0;

16861689

}

1687169016881691

void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {

1689-

conflictCallback = nullptr;

1690-

filterCallback = nullptr;

1692+

ConflictCallbackContext context;

1691169316921694

DatabaseSync* db;

16931695

ASSIGN_OR_RETURN_UNWRAP(&db, args.This());

@@ -1723,7 +1725,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {

17231725

return;

17241726

}

17251727

Local<Function> conflictFunc = conflictValue.As<Function>();

1726-

conflictCallback = [env, conflictFunc](int conflictType) -> int {

1728+

context.conflictCallback = [env, conflictFunc](int conflictType) -> int {

17271729

Local<Value> argv[] = {Integer::New(env->isolate(), conflictType)};

17281730

TryCatch try_catch(env->isolate());

17291731

Local<Value> result =

@@ -1761,15 +1763,18 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {

1761176317621764

Local<Function> filterFunc = filterValue.As<Function>();

176317651764-

filterCallback = [env, filterFunc](std::string item) -> bool {

1766+

context.filterCallback = [env,

1767+

filterFunc](std::string_view item) -> bool {

17651768

// TODO(@jasnell): The use of ToLocalChecked here means that if

17661769

// the filter function throws an error the process will crash.

17671770

// The filterCallback should be updated to avoid the check and

17681771

// propagate the error correctly.

1769-

Local<Value> argv[] = {String::NewFromUtf8(env->isolate(),

1770-

item.c_str(),

1771-

NewStringType::kNormal)

1772-

.ToLocalChecked()};

1772+

Local<Value> argv[] = {

1773+

String::NewFromUtf8(env->isolate(),

1774+

item.data(),

1775+

NewStringType::kNormal,

1776+

static_cast<int>(item.size()))

1777+

.ToLocalChecked()};

17731778

Local<Value> result =

17741779

filterFunc->Call(env->context(), Null(env->isolate()), 1, argv)

17751780

.ToLocalChecked();

@@ -1785,7 +1790,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {

17851790

const_cast<void*>(static_cast<const void*>(buf.data())),

17861791

xFilter,

17871792

xConflict,

1788-

nullptr);

1793+

static_cast<void*>(&context));

17891794

if (r == SQLITE_OK) {

17901795

args.GetReturnValue().Set(true);

17911796

return;