From 54907ac7ba97691fce83078635c465bbf465c855 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:41:30 -0700 Subject: [PATCH 1/2] ffi: throw on allocation failure in toArrayBuffer() The copy path of toArrayBuffer() allocated its backing store with V8's default failure mode, so a large length aborted the process with a fatal out-of-memory error. toBuffer() throws a catchable ERR_MEMORY_ALLOCATION_FAILED for the same input. Allocate with kReturnNull and throw ERR_MEMORY_ALLOCATION_FAILED when the allocation fails. The memory is left uninitialized because memcpy() fills it right after. Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Assisted-by: claude:opus-5.5 --- src/ffi/data.cc | 13 +++++++++++-- test/ffi/test-ffi-memory.js | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ffi/data.cc b/src/ffi/data.cc index cd2a2481ef6e..ca3315d43a35 100644 --- a/src/ffi/data.cc +++ b/src/ffi/data.cc @@ -15,6 +15,8 @@ using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; +using v8::BackingStoreOnFailureMode; using v8::BigInt; using v8::FunctionCallbackInfo; using v8::Integer; @@ -670,8 +672,15 @@ void ToArrayBuffer(const FunctionCallbackInfo& args) { Local ab; if (copy) { - std::unique_ptr store = - ArrayBuffer::NewBackingStore(isolate, len); + std::unique_ptr store = ArrayBuffer::NewBackingStore( + isolate, + len, + BackingStoreInitializationMode::kUninitialized, + BackingStoreOnFailureMode::kReturnNull); + if (!store) [[unlikely]] { + THROW_ERR_MEMORY_ALLOCATION_FAILED(env); + return; + } if (len > 0) memcpy(store->Data(), reinterpret_cast(ptr), len); ab = ArrayBuffer::New(isolate, std::move(store)); } else { diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js index be9160337dd0..ac89c1ebfd74 100644 --- a/test/ffi/test-ffi-memory.js +++ b/test/ffi/test-ffi-memory.js @@ -371,6 +371,16 @@ test('ffi rejects unsafe integers as an offset or length', () => { })); }); +test('ffi toBuffer and toArrayBuffer throw when the copy cannot be allocated', { + skip: (bufferConstants.MAX_LENGTH < 2 ** 50 && 'requires a 64-bit buffer length limit') || + (common.isASan && 'ASan aborts on huge allocations'), +}, () => { + // The allocation fails before the source pointer is read. + const error = { code: 'ERR_MEMORY_ALLOCATION_FAILED' }; + assert.throws(() => ffi.toBuffer(1n, 2 ** 50), error); + assert.throws(() => ffi.toArrayBuffer(1n, 2 ** 50), error); +}); + test('ffi memory helpers reject missing required arguments', () => { const widths = ['Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Float32', 'Float64']; From 1d89180c4544cd56b9f7e04ef9cf2081ee44a69c Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Wed, 30 Sep 2026 07:25:49 -0700 Subject: [PATCH 2/2] fixup! ffi: throw on allocation failure in toArrayBuffer() --- test/ffi/test-ffi-memory.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js index ac89c1ebfd74..1ca54ce897cb 100644 --- a/test/ffi/test-ffi-memory.js +++ b/test/ffi/test-ffi-memory.js @@ -373,7 +373,8 @@ test('ffi rejects unsafe integers as an offset or length', () => { test('ffi toBuffer and toArrayBuffer throw when the copy cannot be allocated', { skip: (bufferConstants.MAX_LENGTH < 2 ** 50 && 'requires a 64-bit buffer length limit') || - (common.isASan && 'ASan aborts on huge allocations'), + (common.isASan && 'ASan aborts on huge allocations') || + (common.isAIX && 'huge allocations may succeed on AIX and get the process killed'), }, () => { // The allocation fails before the source pointer is read. const error = { code: 'ERR_MEMORY_ALLOCATION_FAILED' };