Commit 04904eb4ca4 for nodejs

commit 04904eb4ca488300816cc260cf794598c16a65ab
Author: Trivikram Kamat <trivikr.dev@gmail.com>
Date:   Sat Oct 3 20:56:13 2026 -0700

    ffi: fix exportArrayBuffer type check

    Use isArrayBuffer() instead of comparing Object.prototype.toString()
    output. The old check depended on Symbol.toStringTag, so a real
    ArrayBuffer with a custom tag was rejected, and a plain object tagged
    'ArrayBuffer' passed and failed later in native code with a misleading
    error.

    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/66455
    Fixes: https://github.com/nodejs/node/issues/66454
    Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
    Reviewed-By: Paolo Insogna <paolo@cowtech.it>
    Reviewed-By: Anna Henningsen <anna@addaleax.net>

diff --git a/lib/ffi.js b/lib/ffi.js
index 2a9db50fa51..82663a9710d 100644
--- a/lib/ffi.js
+++ b/lib/ffi.js
@@ -8,7 +8,6 @@ const {
   ObjectFreeze,
   ObjectGetOwnPropertyDescriptor,
   ObjectKeys,
-  ObjectPrototypeToString,
   ReflectConstruct,
   SafeWeakMap,
   SafeWeakRef,
@@ -18,6 +17,7 @@ const {
 const { Buffer } = require('buffer');
 const { emitExperimentalWarning } = require('internal/util');
 const {
+  isArrayBuffer,
   isDataView,
   isArrayBufferView,
   isSharedArrayBuffer,
@@ -311,7 +311,7 @@ function exportBuffer(source, data, len) {
 function exportArrayBuffer(source, data, len) {
   checkFFIPermission();

-  if (ObjectPrototypeToString(source) !== '[object ArrayBuffer]') {
+  if (!isArrayBuffer(source)) {
     throw new ERR_INVALID_ARG_TYPE('arrayBuffer', 'ArrayBuffer', source);
   }

diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js
index 1ca54ce897c..3bcda27e176 100644
--- a/test/ffi/test-ffi-memory.js
+++ b/test/ffi/test-ffi-memory.js
@@ -238,6 +238,11 @@ test('ffi exportString and exportBuffer copy data into native memory', () => {
     ffi.exportArrayBuffer(arrayBuffer, arrayBufferPtr, 4);
     assert.deepStrictEqual([...ffi.toBuffer(arrayBufferPtr, 4)], [8, 9, 10, 11]);

+    const taggedArrayBuffer = new Uint8Array([12, 13, 14, 15]).buffer;
+    Object.defineProperty(taggedArrayBuffer, Symbol.toStringTag, { value: 'Custom' });
+    ffi.exportArrayBuffer(taggedArrayBuffer, arrayBufferPtr, 4);
+    assert.deepStrictEqual([...ffi.toBuffer(arrayBufferPtr, 4)], [12, 13, 14, 15]);
+
     const viewPtr = alloc(8);
     const viewSource = new Uint16Array([0x0102, 0x0304, 0x0506]);
     const middleBytes = new Uint8Array(viewSource.buffer, 2, 2);
@@ -318,6 +323,10 @@ test('ffi validates memory access arguments', () => {
     assert.throws(() => ffi.exportBuffer(Buffer.from([1]), ptr, -1), { code: 'ERR_OUT_OF_RANGE' });
     assert.throws(() => ffi.exportBuffer(Buffer.from([1, 2]), ptr, 1), { code: 'ERR_OUT_OF_RANGE' });
     assert.throws(() => ffi.exportArrayBuffer('bad', ptr, 4), { code: 'ERR_INVALID_ARG_TYPE' });
+    assert.throws(() => ffi.exportArrayBuffer({ [Symbol.toStringTag]: 'ArrayBuffer', byteLength: 1 }, ptr, 4), {
+      code: 'ERR_INVALID_ARG_TYPE',
+      message: /The "arrayBuffer" argument must be an instance of ArrayBuffer/,
+    });
     assert.throws(() => ffi.exportArrayBuffer(new ArrayBuffer(1), ptr, -1), { code: 'ERR_OUT_OF_RANGE' });
     assert.throws(() => ffi.exportArrayBuffer(new ArrayBuffer(2), ptr, 1), { code: 'ERR_OUT_OF_RANGE' });
     assert.throws(() => ffi.exportArrayBufferView('bad', ptr, 4), { code: 'ERR_INVALID_ARG_TYPE' });