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' });