Commit de073c56faf for nodejs
commit de073c56faf955fc69dc2db60d1bf5e6d0af2e33
Author: Trivikram Kamat <trivikr.dev@gmail.com>
Date: Fri Oct 2 00:34:55 2026 -0700
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
PR-URL: https://github.com/nodejs/node/pull/66406
Fixes: https://github.com/nodejs/node/issues/66405
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
diff --git a/src/ffi/data.cc b/src/ffi/data.cc
index cd2a2481ef6..ca3315d43a3 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<Value>& args) {
Local<ArrayBuffer> ab;
if (copy) {
- std::unique_ptr<BackingStore> store =
- ArrayBuffer::NewBackingStore(isolate, len);
+ std::unique_ptr<BackingStore> 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<void*>(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 be9160337dd..1ca54ce897c 100644
--- a/test/ffi/test-ffi-memory.js
+++ b/test/ffi/test-ffi-memory.js
@@ -371,6 +371,17 @@ 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.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' };
+ 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'];