From abb365abea80915022c81908bea42d321d8377f4 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 27 Aug 2026 00:00:51 -0700 Subject: [PATCH] ffi: validate DynamicLibrary getter receivers Bind the path, symbols, and functions getter templates to the DynamicLibrary constructor signature. This causes V8 to reject incompatible receivers before invoking the native callbacks, preventing them from crashing the process. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol PR-URL: https://github.com/nodejs/node/pull/65415 Fixes: https://github.com/nodejs/node/issues/65287 Reviewed-By: Paolo Insogna --- src/node_ffi.cc | 11 ++++++++--- test/ffi/test-ffi-dynamic-library.js | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 7e3da95b958..88327f1f1c4 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -35,6 +35,7 @@ using v8::MaybeLocal; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; +using v8::Signature; using v8::String; using v8::TryCatch; using v8::Value; @@ -1238,16 +1239,19 @@ Local DynamicLibrary::GetConstructorTemplate( tmpl = NewFunctionTemplate(isolate, DynamicLibrary::New); tmpl->InstanceTemplate()->SetInternalFieldCount( DynamicLibrary::kInternalFieldCount); + Local signature = Signature::New(isolate, tmpl); tmpl->InstanceTemplate()->SetAccessorProperty( env->path_string(), - FunctionTemplate::New(env->isolate(), DynamicLibrary::GetPath), + FunctionTemplate::New( + isolate, DynamicLibrary::GetPath, Local(), signature), Local(), attributes); tmpl->InstanceTemplate()->SetAccessorProperty( FIXED_ONE_BYTE_STRING(isolate, "symbols"), - FunctionTemplate::New(env->isolate(), DynamicLibrary::GetSymbols), + FunctionTemplate::New( + isolate, DynamicLibrary::GetSymbols, Local(), signature), Local(), attributes); @@ -1257,7 +1261,8 @@ Local DynamicLibrary::GetConstructorTemplate( // reason. tmpl->PrototypeTemplate()->SetAccessorProperty( FIXED_ONE_BYTE_STRING(isolate, "functions"), - FunctionTemplate::New(env->isolate(), DynamicLibrary::GetFunctions), + FunctionTemplate::New( + isolate, DynamicLibrary::GetFunctions, Local(), signature), Local(), static_cast(ReadOnly)); diff --git a/test/ffi/test-ffi-dynamic-library.js b/test/ffi/test-ffi-dynamic-library.js index c7dab03f64e..1fb444eba5c 100644 --- a/test/ffi/test-ffi-dynamic-library.js +++ b/test/ffi/test-ffi-dynamic-library.js @@ -124,6 +124,24 @@ test('DynamicLibrary exposes functions and symbols', () => { } }); +test('DynamicLibrary getters reject incompatible receivers', () => { + const lib = new ffi.DynamicLibrary(libraryPath); + + try { + const invalidGets = [ + () => Reflect.get(lib, 'path', {}), + () => Reflect.get(lib, 'symbols', {}), + () => Reflect.get(ffi.DynamicLibrary.prototype, 'functions', {}), + ]; + + for (const invalidGet of invalidGets) { + assert.throws(invalidGet, TypeError); + } + } finally { + lib.close(); + } +}); + test('DynamicLibrary evaluates function signatures once', () => { function makeChangingSignature() { const reads = { arguments: 0, return: 0 };