Skip to content

Commit f4eb9bd

Browse files
committed
src: enforce FFI permission on DynamicLibrary instance methods
The DynamicLibrary::New constructor checks permission::PermissionScope::kFFI, but the instance methods (InvokeFunction, GetFunction, GetFunctions, GetSymbol, GetSymbols, RegisterCallback, UnregisterCallback, RefCallback, UnrefCallback, Close) did not, creating a defense-in-depth gap. An attacker able to obtain a DynamicLibrary handle through shared state (e.g. a leaked reference from trusted code) could invoke arbitrary native functions, resolve symbols, register executable callback trampolines, and otherwise perform FFI operations even though --allow-ffi was not granted. The critical gap was InvokeFunction: once a function handle was created, executing it bypassed the permission model entirely. The audit mode also failed to surface these violations. Add THROW_IF_INSUFFICIENT_PERMISSIONS to every instance method, matching the defense-in-depth pattern already used by the raw memory helpers in src/ffi/data.cc (GetInt*, SetInt*, ToString, ToBuffer, ToArrayBuffer). Refs: nodejs#62072
1 parent bee1087 commit f4eb9bd

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

src/node_ffi.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ void DynamicLibrary::New(const FunctionCallbackInfo<Value>& args) {
261261
}
262262

263263
void DynamicLibrary::Close(const FunctionCallbackInfo<Value>& args) {
264+
Environment* env = Environment::GetCurrent(args);
265+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
264266
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
265267
// Closing a library from one of its active callbacks is unsupported and
266268
// dangerous. Callbacks must return before the owning library is closed.
@@ -269,6 +271,7 @@ void DynamicLibrary::Close(const FunctionCallbackInfo<Value>& args) {
269271

270272
void DynamicLibrary::InvokeFunction(const FunctionCallbackInfo<Value>& args) {
271273
Environment* env = Environment::GetCurrent(args);
274+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
272275
FFIFunctionInfo* info =
273276
static_cast<FFIFunctionInfo*>(args.Data().As<External>()->Value());
274277
FFIFunction* fn = info->fn.get();
@@ -431,6 +434,7 @@ void DynamicLibrary::GetPath(const FunctionCallbackInfo<Value>& args) {
431434

432435
void DynamicLibrary::GetFunction(const FunctionCallbackInfo<Value>& args) {
433436
Environment* env = Environment::GetCurrent(args);
437+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
434438
Isolate* isolate = env->isolate();
435439

436440
if (args.Length() < 1 || !args[0]->IsString()) {
@@ -479,6 +483,7 @@ void DynamicLibrary::GetFunction(const FunctionCallbackInfo<Value>& args) {
479483

480484
void DynamicLibrary::GetFunctions(const FunctionCallbackInfo<Value>& args) {
481485
Environment* env = Environment::GetCurrent(args);
486+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
482487
Isolate* isolate = env->isolate();
483488
Local<Context> context = env->context();
484489
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
@@ -609,6 +614,7 @@ void DynamicLibrary::GetFunctions(const FunctionCallbackInfo<Value>& args) {
609614

610615
void DynamicLibrary::GetSymbol(const FunctionCallbackInfo<Value>& args) {
611616
Environment* env = Environment::GetCurrent(args);
617+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
612618
Isolate* isolate = env->isolate();
613619

614620
if (args.Length() < 1 || !args[0]->IsString()) {
@@ -635,6 +641,7 @@ void DynamicLibrary::GetSymbol(const FunctionCallbackInfo<Value>& args) {
635641

636642
void DynamicLibrary::GetSymbols(const FunctionCallbackInfo<Value>& args) {
637643
Environment* env = Environment::GetCurrent(args);
644+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
638645
Isolate* isolate = env->isolate();
639646
Local<Context> context = env->context();
640647
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
@@ -673,6 +680,7 @@ void DynamicLibrary::GetSymbols(const FunctionCallbackInfo<Value>& args) {
673680

674681
void DynamicLibrary::RegisterCallback(const FunctionCallbackInfo<Value>& args) {
675682
Environment* env = Environment::GetCurrent(args);
683+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
676684
Isolate* isolate = env->isolate();
677685

678686
ffi_type* return_type = &ffi_type_void;
@@ -793,6 +801,7 @@ void DynamicLibrary::RegisterCallback(const FunctionCallbackInfo<Value>& args) {
793801
void DynamicLibrary::UnregisterCallback(
794802
const FunctionCallbackInfo<Value>& args) {
795803
Environment* env = Environment::GetCurrent(args);
804+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
796805
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
797806

798807
if (lib->handle_ == nullptr) {
@@ -828,6 +837,7 @@ void DynamicLibrary::UnregisterCallback(
828837

829838
void DynamicLibrary::RefCallback(const FunctionCallbackInfo<Value>& args) {
830839
Environment* env = Environment::GetCurrent(args);
840+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
831841
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
832842

833843
if (lib->handle_ == nullptr) {
@@ -858,6 +868,7 @@ void DynamicLibrary::RefCallback(const FunctionCallbackInfo<Value>& args) {
858868

859869
void DynamicLibrary::UnrefCallback(const FunctionCallbackInfo<Value>& args) {
860870
Environment* env = Environment::GetCurrent(args);
871+
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
861872
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
862873

863874
if (lib->handle_ == nullptr) {

0 commit comments

Comments
 (0)