From e6bf923186a38c672b0c8326cdbe8932a19a42f3 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:47:18 -0700 Subject: [PATCH] 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 --- lib/ffi.js | 4 ++-- test/ffi/test-ffi-memory.js | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/ffi.js b/lib/ffi.js index 5cd7c4b354ab..bef758811e7d 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, @@ -313,7 +313,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 be9160337dd0..df9422a93dd0 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' });