diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 732657a368ef..7e8ad60aa4d6 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -144,12 +144,14 @@ Maybe DynamicLibrary::ResolveSymbol(Environment* env, Maybe DynamicLibrary::PrepareFunction( Environment* env, const std::string& name, Local signature) { std::shared_ptr fn; - auto existing = functions_.find(name); FunctionSignature parsed; if (!ParseFunctionSignature(env, name, signature).To(&parsed)) { return {}; } + // Look up the cache only after parsing: the signature's getters run user + // code that may close the library, which clears `functions_`. + auto existing = functions_.find(name); auto [return_type, args, return_type_name, arg_type_names] = std::move(parsed); diff --git a/test/ffi/test-ffi-dynamic-library.js b/test/ffi/test-ffi-dynamic-library.js index 3240f194972c..62cc7e0b4d49 100644 --- a/test/ffi/test-ffi-dynamic-library.js +++ b/test/ffi/test-ffi-dynamic-library.js @@ -290,6 +290,21 @@ test('closed libraries reject subsequent operations', () => { assert.throws(() => lib.getSymbols(), /Library is closed/); }); +test('closing the library from a signature getter of a cached function', () => { + const lib = new ffi.DynamicLibrary(libraryPath); + lib.getFunction('add_i32', fixtureSymbols.add_i32); + + assert.throws(() => { + lib.getFunction('add_i32', { + arguments: ['i32', 'i32'], + get return() { + lib.close(); + return 'i32'; + }, + }); + }, { code: 'ERR_FFI_LIBRARY_CLOSED' }); +}); + test('optimized fast calls reject calls after the library is closed', () => { const { lib, functions } = ffi.dlopen(libraryPath, { multiply_f64: fixtureSymbols.multiply_f64,