diff --git a/src/ffi/data.cc b/src/ffi/data.cc index 73b575395c8c..6a8d54ca0d39 100644 --- a/src/ffi/data.cc +++ b/src/ffi/data.cc @@ -685,7 +685,7 @@ void ExportBytes(const FunctionCallbackInfo& args) { args[0]->IsArrayBufferView()) { view.ReadValue(args[0]); if (view.WasDetached()) { - THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store"); + THROW_ERR_INVALID_ARG_VALUE(env, "ArrayBuffer is detached"); return; } } else { @@ -749,15 +749,26 @@ void GetRawPointer(const FunctionCallbackInfo& args) { std::shared_ptr store; if (args[0]->IsArrayBuffer()) { - store = args[0].As()->GetBackingStore(); + Local buffer = args[0].As(); + if (buffer->WasDetached()) { + THROW_ERR_INVALID_ARG_VALUE(env, "ArrayBuffer is detached"); + return; + } + store = buffer->GetBackingStore(); } else if (args[0]->IsSharedArrayBuffer()) { store = args[0].As()->GetBackingStore(); } else if (args[0]->IsArrayBufferView()) { + Local view = args[0].As(); + if (view->Buffer()->WasDetached()) { + THROW_ERR_INVALID_ARG_VALUE( + env, "ArrayBufferView is backed by a detached ArrayBuffer"); + return; + } // Access the store here to ensure that it exists. Small typed arrays // may not have a store until this point and can instead be stored // entirely in-heap. - store = args[0].As()->Buffer()->GetBackingStore(); - offset = args[0].As()->ByteOffset(); + store = view->Buffer()->GetBackingStore(); + offset = view->ByteOffset(); } else { THROW_ERR_INVALID_ARG_TYPE( env, diff --git a/src/ffi/types.cc b/src/ffi/types.cc index 9ba3cc4da448..db0c913c547d 100644 --- a/src/ffi/types.cc +++ b/src/ffi/types.cc @@ -700,6 +700,14 @@ Maybe ToFFIArgument(Environment* env, // invalidating that backing store during the active FFI call is // unsupported and dangerous. Local view = arg.As(); + if (view->Buffer()->WasDetached()) { + THROW_ERR_INVALID_ARG_VALUE( + env, + "Argument %u is an ArrayBufferView backed by a detached " + "ArrayBuffer", + index); + return {}; + } std::shared_ptr store = view->Buffer()->GetBackingStore(); if (!store) { @@ -721,6 +729,11 @@ Maybe ToFFIArgument(Environment* env, // that backing store during the active FFI call is unsupported and // dangerous. Local buffer = arg.As(); + if (buffer->WasDetached()) { + THROW_ERR_INVALID_ARG_VALUE( + env, "Argument %u is a detached ArrayBuffer", index); + return {}; + } std::shared_ptr store = buffer->GetBackingStore(); if (!store) { diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js index f17f56c410f8..ee88e9ef4151 100644 --- a/test/ffi/test-ffi-memory.js +++ b/test/ffi/test-ffi-memory.js @@ -146,6 +146,41 @@ test('ffi getRawPointer returns raw addresses for byte sources', () => { assert.strictEqual(sharedViewPointer, sharedArrayBufferPointer + 2n); }); +test('ffi rejects detached array buffers and views as pointers', () => { + const arrayBuffer = new ArrayBuffer(8); + const typedArray = new Uint8Array(arrayBuffer); + const dataView = new DataView(arrayBuffer); + + arrayBuffer.transfer(); + + assert.throws(() => ffi.exportArrayBuffer(arrayBuffer, 0n, 0), { + code: 'ERR_INVALID_ARG_VALUE', + message: 'ArrayBuffer is detached', + }); + + for (const [value, rawPointerMessage, argumentMessage] of [ + [ + arrayBuffer, + 'ArrayBuffer is detached', + 'Argument 0 is a detached ArrayBuffer', + ], + ...[typedArray, dataView].map((view) => [ + view, + 'ArrayBufferView is backed by a detached ArrayBuffer', + 'Argument 0 is an ArrayBufferView backed by a detached ArrayBuffer', + ]), + ]) { + assert.throws(() => ffi.getRawPointer(value), { + code: 'ERR_INVALID_ARG_VALUE', + message: rawPointerMessage, + }); + assert.throws(() => symbols.pointer_to_usize(value), { + code: 'ERR_INVALID_ARG_VALUE', + message: argumentMessage, + }); + } +}); + test('ffi exportString and exportBuffer copy data into native memory', () => { withAllocations(common.mustCall((alloc) => { const stringPtr = alloc(16);