Repository: nodejs/node
Pull request: src: throw RangeError on failed ArrayBuffer BackingStore allocation

This also updates `ERR_MEMORY_ALLOCATION_FAILED` to be a RangeError,
aligning with V8's OutOfMemory error type and ECMAScript's OutOfMemory
error type.

There are still many call sites in crypto, tls and quic need to be reviewed
to properly handle the failed ArrayBuffer BackingStore allocation. This
PR updates call sites not in these modules.

Refs: https://github.com/nodejs/node/blob/c755b0113ce0cb6d83baf2cf070ba381a5673db2/deps/v8/src/builtins/builtins-typed-array.cc#L584
Refs: https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.grow
Refs: https://github.com/nodejs/node/pull/61403#discussion_r2712909264

Changed files:
filename: src/encoding_binding.cc
patch: @@ -17,6 +17,7 @@ namespace encoding_binding {
 using v8::ArrayBuffer;
 using v8::BackingStore;
 using v8::BackingStoreInitializationMode;
+using v8::BackingStoreOnFailureMode;
 using v8::Context;
 using v8::FunctionCallbackInfo;
 using v8::HandleScope;
@@ -317,9 +318,15 @@ void BindingData::EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
   Local<ArrayBuffer> ab;
   {
     std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
-        isolate, length, BackingStoreInitializationMode::kUninitialized);
+        isolate,
+        length,
+        BackingStoreInitializationMode::kUninitialized,
+        BackingStoreOnFailureMode::kReturnNull);
 
-    CHECK(bs);
+    if (!bs) [[unlikely]] {
+      THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
+      return;
+    }
 
     // We are certain that `data` is sufficiently large
     str->WriteUtf8V2(isolate,

filename: src/node_blob.cc
patch: @@ -22,6 +22,7 @@ using v8::ArrayBuffer;
 using v8::ArrayBufferView;
 using v8::BackingStore;
 using v8::BackingStoreInitializationMode;
+using v8::BackingStoreOnFailureMode;
 using v8::Context;
 using v8::Function;
 using v8::FunctionCallbackInfo;
@@ -83,7 +84,14 @@ void Concat(const FunctionCallbackInfo<Value>& args) {
   }
 
   std::shared_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
-      isolate, total, BackingStoreInitializationMode::kUninitialized);
+      isolate,
+      total,
+      BackingStoreInitializationMode::kUninitialized,
+      BackingStoreOnFailureMode::kReturnNull);
+  if (!store) [[unlikely]] {
+    THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
+    return;
+  }
   uint8_t* ptr = static_cast<uint8_t*>(store->Data());
   for (size_t n = 0; n < views.size(); n++) {
     uint8_t* from =

filename: src/node_buffer.cc
patch: @@ -59,6 +59,7 @@ using v8::ArrayBuffer;
 using v8::ArrayBufferView;
 using v8::BackingStore;
 using v8::BackingStoreInitializationMode;
+using v8::BackingStoreOnFailureMode;
 using v8::CFunction;
 using v8::Context;
 using v8::EscapableHandleScope;
@@ -304,17 +305,20 @@ MaybeLocal<Object> New(Isolate* isolate,
   EscapableHandleScope scope(isolate);
 
   size_t length;
-  if (!StringBytes::Size(isolate, string, enc).To(&length))
-    return Local<Object>();
+  if (!StringBytes::Size(isolate, string, enc).To(&length)) return {};
   size_t actual = 0;
   std::unique_ptr<BackingStore> store;
 
   if (length > 0) {
-    store = ArrayBuffer::NewBackingStore(isolate, length);
+    store = ArrayBuffer::NewBackingStore(
+        isolate,
+        length,
+        BackingStoreInitializationMode::kZeroInitialized,
+        BackingStoreOnFailureMode::kReturnNull);
 
     if (!store) [[unlikely]] {
       THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
-      return Local<Object>();
+      return {};
     }
 
     actual = StringBytes::Write(
@@ -329,7 +333,14 @@ MaybeLocal<Object> New(Isolate* isolate,
       if (actual < length) {
         std::unique_ptr<BackingStore> old_store = std::move(store);
         store = ArrayBuffer::NewBackingStore(
-            isolate, actual, BackingStoreInitializationMode::kUninitialized);
+            isolate,
+            actual,
+            BackingStoreInitializationMode::kUninitialized,
+            BackingStoreOnFailureMode::kReturnNull);
+        if (!store) [[unlikely]] {
+          THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
+          return {};
+        }
         memcpy(store->Data(), old_store->Data(), actual);
       }
       Local<ArrayBuffer> buf = ArrayBuffer::New(isolate, std::move(store));
@@ -372,7 +383,14 @@ MaybeLocal<Object> New(Environment* env, size_t length) {
   Local<ArrayBuffer> ab;
   {
     std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
-        isolate, length, BackingStoreInitializationMode::kUninitialized);
+        isolate,
+        length,
+        BackingStoreInitializationMode::kUninitialized,
+        BackingStoreOnFailureMode::kReturnNull);
+    if (!bs) [[unlikely]] {
+      THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
+      return {};
+    }
 
     CHECK(bs);
 
@@ -412,9 +430,14 @@ MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
   }
 
   std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
-      isolate, length, BackingStoreInitializationMode::kUninitialized);
-
-  CHECK(bs);
+      isolate,
+      length,
+      BackingStoreInitializationMode::kUninitialized,
+      BackingStoreOnFailureMode::kReturnNull);
+  if (!bs) [[unlikely]] {
+    THROW_ERR_MEMORY_ALLOCATION_FAILED(isolate);
+    return {};
+  }
 
   if (length > 0) memcpy(bs->Data(), data, length);
 
@@ -1449,8 +1472,8 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
         BackingStoreInitializationMode::kUninitialized,
         v8::BackingStoreOnFailureMode::kReturnNull);
 
-    if (!store) {
-      env->ThrowRangeError("Array buffer allocation failed");
+    if (!store) [[unlikely]] {
+      THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
       return;
     }
 

filename: src/node_errors.h
patch: @@ -106,7 +106,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
   V(ERR_INVALID_URL_PATTERN, TypeError)                                        \
   V(ERR_INVALID_URL_SCHEME, TypeError)                                         \
   V(ERR_LOAD_SQLITE_EXTENSION, Error)                                          \
-  V(ERR_MEMORY_ALLOCATION_FAILED, Error)                                       \
+  V(ERR_MEMORY_ALLOCATION_FAILED, RangeError)                                  \
   V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, Error)                             \
   V(ERR_MISSING_ARGS, TypeError)                                               \
   V(ERR_MISSING_PASSPHRASE, TypeError)                                         \