Commit f71d6441ced for nodejs

commit f71d6441ced93dd6f5e4386c5fb4de0fe146768a
Author: Trivikram Kamat <trivikr.dev@gmail.com>
Date:   Tue Sep 29 20:33:37 2026 -0700

    ffi: fix use-after-free in PrepareFunction

    PrepareFunction() looked up the function cache before parsing the
    signature. Parsing can run user getters, and a getter that calls
    lib.close() clears the cache, which invalidates the iterator. For a
    function that was already cached, reading it afterwards used freed
    memory and crashed the process.

    Look up the cache after the signature is parsed. If the library was
    closed during parsing, ResolveSymbol() now throws
    ERR_FFI_LIBRARY_CLOSED.

    Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
    Assisted-by: claude:opus-5.5
    PR-URL: https://github.com/nodejs/node/pull/66368
    Fixes: https://github.com/nodejs/node/issues/66367
    Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
    Reviewed-By: Paolo Insogna <paolo@cowtech.it>
    Reviewed-By: Anna Henningsen <anna@addaleax.net>
    Reviewed-By: Colin Ihrig <cjihrig@gmail.com>

diff --git a/src/node_ffi.cc b/src/node_ffi.cc
index 732657a368e..7e8ad60aa4d 100644
--- a/src/node_ffi.cc
+++ b/src/node_ffi.cc
@@ -144,12 +144,14 @@ Maybe<void*> DynamicLibrary::ResolveSymbol(Environment* env,
 Maybe<DynamicLibrary::PreparedFunction> DynamicLibrary::PrepareFunction(
     Environment* env, const std::string& name, Local<Object> signature) {
   std::shared_ptr<FFIFunction> 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 3240f194972..62cc7e0b4d4 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,