From 6a4e4e3821721f9aee4cfe85ac75f257434058f8 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Wed, 21 Jan 2026 19:43:39 +0900 Subject: [PATCH] fixup! chore:remove zero-fill sandbox patch component --- patches/node/.patches | 2 + ..._obsolete_noarraybufferzerofillscope.patch | 653 ++++++++++++++++++ .../node/src_prepare_for_v8_sandboxing.patch | 276 ++++++++ 3 files changed, 931 insertions(+) create mode 100644 patches/node/remove_obsolete_noarraybufferzerofillscope.patch create mode 100644 patches/node/src_prepare_for_v8_sandboxing.patch diff --git a/patches/node/.patches b/patches/node/.patches index 9619bf6de1..c9c20ec213 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -54,3 +54,5 @@ fix_redefined_macos_sdk_header_symbols.patch src_use_cp_utf8_for_wide_file_names_on_win32.patch fix_ensure_traverseparent_bails_on_resource_path_exit.patch src_handle_der_decoding_errors_from_system_certificates.patch +remove_obsolete_noarraybufferzerofillscope.patch +src_prepare_for_v8_sandboxing.patch diff --git a/patches/node/remove_obsolete_noarraybufferzerofillscope.patch b/patches/node/remove_obsolete_noarraybufferzerofillscope.patch new file mode 100644 index 0000000000..6cf3258273 --- /dev/null +++ b/patches/node/remove_obsolete_noarraybufferzerofillscope.patch @@ -0,0 +1,653 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Robo +Date: Wed, 21 Jan 2026 09:53:15 +0000 +Subject: remove obsolete NoArrayBufferZeroFillScope + +Replace the scope in favor of the V8 api added in +https://chromium-review.googlesource.com/c/v8/v8/+/5679067 + +Ports changes from +1) https://github.com/nodejs/node/commit/869ea331f3a8215229290e2e6038956874c382a6 +2) https://github.com/nodejs/node/commit/ef9dc0857a73610f5de5dc9f37afd0a927c4c17f +3) partially from https://github.com/nodejs/node/commit/e0a71517fef4ca83f2d40d2d1600022bc82a7f9f + +This is needed to remove dependency on the zero_fill_field_ +that is exposed to JS +Refs https://github.com/nodejs/node/commit/3cdb1cd437f63dd256ae2ab3b7e9016257326cb4 + +diff --git a/src/api/environment.cc b/src/api/environment.cc +index 8a12475857135bd3e904610b7eb887c397c8e73c..3702672f4e2c3b1bff9f9f5c66ca251af447826a 100644 +--- a/src/api/environment.cc ++++ b/src/api/environment.cc +@@ -107,11 +107,7 @@ MaybeLocal PrepareStackTraceCallback(Local context, + } + + void* NodeArrayBufferAllocator::Allocate(size_t size) { +- void* ret; +- if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) +- ret = allocator_->Allocate(size); +- else +- ret = allocator_->AllocateUninitialized(size); ++ void* ret = allocator_->Allocate(size); + if (ret != nullptr) [[likely]] { + total_mem_usage_.fetch_add(size, std::memory_order_relaxed); + } +diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc +index c00d3616e08b00b1e0a3a29b2dbb5278e1e14fcc..8939c5e5085d00b098f66074b9ee033f5be55d08 100644 +--- a/src/crypto/crypto_cipher.cc ++++ b/src/crypto/crypto_cipher.cc +@@ -20,6 +20,7 @@ using ncrypto::SSLPointer; + using v8::Array; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Context; + using v8::FunctionCallbackInfo; + using v8::FunctionTemplate; +@@ -774,10 +775,10 @@ CipherBase::UpdateResult CipherBase::Update( + return kErrorState; + } + +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); +- *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); +- } ++ *out = ArrayBuffer::NewBackingStore( ++ env()->isolate(), ++ buf_len, ++ BackingStoreInitializationMode::kUninitialized); + + buffer = { + .data = reinterpret_cast(data), +@@ -852,11 +853,10 @@ bool CipherBase::Final(std::unique_ptr* out) { + + const int mode = ctx_.getMode(); + +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); +- *out = ArrayBuffer::NewBackingStore( +- env()->isolate(), static_cast(ctx_.getBlockSize())); +- } ++ *out = ArrayBuffer::NewBackingStore( ++ env()->isolate(), ++ static_cast(ctx_.getBlockSize()), ++ BackingStoreInitializationMode::kUninitialized); + + if (kind_ == kDecipher && + Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { +@@ -972,10 +972,10 @@ bool PublicKeyCipher::Cipher( + return false; + } + +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); +- } ++ *out = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ out_len, ++ BackingStoreInitializationMode::kUninitialized); + + if (EVP_PKEY_cipher( + ctx.get(), +diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc +index b81b9005365272217c77e2b9289bd9f877c0e77c..185b1d8fe657b5db64dc92497ca742d69f7a2764 100644 +--- a/src/crypto/crypto_common.cc ++++ b/src/crypto/crypto_common.cc +@@ -37,6 +37,7 @@ using ncrypto::X509Pointer; + using ncrypto::X509View; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Context; + using v8::EscapableHandleScope; + using v8::Integer; +@@ -307,11 +308,10 @@ MaybeLocal ECPointToBuffer(Environment* env, + return MaybeLocal(); + } + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), len); +- } ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ len, ++ BackingStoreInitializationMode::kUninitialized); + + len = EC_POINT_point2oct(group, + point, +diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc +index 2a3107dbbf5c0dfe70c2e105338d186e5620ddbf..f36c84a77313bd57d0a7a902d1a2529636ca1422 100644 +--- a/src/crypto/crypto_ec.cc ++++ b/src/crypto/crypto_ec.cc +@@ -29,6 +29,7 @@ using ncrypto::MarkPopErrorOnReturn; + using v8::Array; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Context; + using v8::FunctionCallbackInfo; + using v8::FunctionTemplate; +@@ -201,14 +202,10 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { + return; + } + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- // NOTE: field_size is in bits +- int field_size = EC_GROUP_get_degree(ecdh->group_); +- size_t out_len = (field_size + 7) / 8; +- bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len); +- } ++ int field_size = EC_GROUP_get_degree(ecdh->group_); ++ size_t out_len = (field_size + 7) / 8; ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), out_len, BackingStoreInitializationMode::kUninitialized); + + if (!ECDH_compute_key( + bs->Data(), bs->ByteLength(), pub, ecdh->key_.get(), nullptr)) +@@ -257,12 +254,10 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "Failed to get ECDH private key"); + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), +- BignumPointer::GetByteCount(b)); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ BignumPointer::GetByteCount(b), ++ BackingStoreInitializationMode::kUninitialized); + CHECK_EQ(bs->ByteLength(), + BignumPointer::EncodePaddedInto( + b, static_cast(bs->Data()), bs->ByteLength())); +diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc +index 1f2fccce6ed8f14525557644e0bdd130eedf3337..1099a8f89bb53083f01942ee14fff453d8cdc0af 100644 +--- a/src/crypto/crypto_rsa.cc ++++ b/src/crypto/crypto_rsa.cc +@@ -20,6 +20,7 @@ using ncrypto::EVPKeyPointer; + using ncrypto::RSAPointer; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::FunctionCallbackInfo; + using v8::Int32; + using v8::JustVoid; +@@ -535,12 +536,10 @@ Maybe GetRsaKeyDetail(Environment* env, + return Nothing(); + } + +- std::unique_ptr public_exponent; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- public_exponent = ArrayBuffer::NewBackingStore( +- env->isolate(), BignumPointer::GetByteCount(e)); +- } ++ auto public_exponent = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ BignumPointer::GetByteCount(e), ++ BackingStoreInitializationMode::kUninitialized); + CHECK_EQ(BignumPointer::EncodePaddedInto( + e, + static_cast(public_exponent->Data()), +diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc +index 2f6e683e3497d4315259773d09443e5215bff28f..c33e93c34ef32c18e6de6bc03698ed6b851b4aa3 100644 +--- a/src/crypto/crypto_sig.cc ++++ b/src/crypto/crypto_sig.cc +@@ -21,6 +21,7 @@ using ncrypto::EVPKeyPointer; + using ncrypto::EVPMDCtxPointer; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Boolean; + using v8::FunctionCallbackInfo; + using v8::FunctionTemplate; +@@ -92,11 +93,8 @@ std::unique_ptr Node_SignFinal(Environment* env, + return nullptr; + + size_t sig_len = pkey.size(); +- std::unique_ptr sig; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); +- } ++ auto sig = ArrayBuffer::NewBackingStore( ++ env->isolate(), sig_len, BackingStoreInitializationMode::kUninitialized); + EVPKeyCtxPointer pkctx = pkey.newCtx(); + if (pkctx && EVP_PKEY_sign_init(pkctx.get()) > 0 && + ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && +@@ -168,11 +166,9 @@ std::unique_ptr ConvertSignatureToP1363( + if (n == kNoDsaSignature) + return std::move(signature); + +- std::unique_ptr buf; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- buf = ArrayBuffer::NewBackingStore(env->isolate(), 2 * n); +- } ++ auto buf = ArrayBuffer::NewBackingStore( ++ env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized); ++ + if (!ExtractP1363(static_cast(signature->Data()), + static_cast(buf->Data()), + signature->ByteLength(), n)) +diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc +index a80685790bd29102d99929ff81f866e0aee370f1..24336b86f6f25533a7b668e9f9806a5635e3a398 100644 +--- a/src/crypto/crypto_tls.cc ++++ b/src/crypto/crypto_tls.cc +@@ -46,6 +46,7 @@ using v8::Array; + using v8::ArrayBuffer; + using v8::ArrayBufferView; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Boolean; + using v8::Context; + using v8::DontDelete; +@@ -1087,10 +1088,10 @@ int TLSWrap::DoWrite(WriteWrap* w, + // and copying it when it could just be used. + + if (nonempty_count != 1) { +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); +- } ++ bs = ArrayBuffer::NewBackingStore( ++ env()->isolate(), ++ length, ++ BackingStoreInitializationMode::kUninitialized); + size_t offset = 0; + for (i = 0; i < count; i++) { + memcpy(static_cast(bs->Data()) + offset, +@@ -1107,8 +1108,10 @@ int TLSWrap::DoWrite(WriteWrap* w, + written = SSL_write(ssl_.get(), buf->base, buf->len); + + if (written == -1) { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); ++ bs = ArrayBuffer::NewBackingStore( ++ env()->isolate(), ++ length, ++ BackingStoreInitializationMode::kUninitialized); + memcpy(bs->Data(), buf->base, buf->len); + } + } +@@ -1746,11 +1749,8 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { + if (len == 0) + return; + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), len); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), len, BackingStoreInitializationMode::kUninitialized); + + CHECK_EQ(bs->ByteLength(), + SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); +@@ -1777,11 +1777,8 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { + if (len == 0) + return; + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), len); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), len, BackingStoreInitializationMode::kUninitialized); + + CHECK_EQ(bs->ByteLength(), + SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); +@@ -1806,11 +1803,8 @@ void TLSWrap::GetSession(const FunctionCallbackInfo& args) { + if (slen <= 0) + return; // Invalid or malformed session. + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), slen); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), slen, BackingStoreInitializationMode::kUninitialized); + + unsigned char* p = static_cast(bs->Data()); + CHECK_LT(0, i2d_SSL_SESSION(sess, &p)); +@@ -1993,11 +1987,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { + uint32_t olen = args[0].As()->Value(); + Utf8Value label(env->isolate(), args[1]); + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), olen); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), olen, BackingStoreInitializationMode::kUninitialized); + + ByteSource context; + bool use_context = !args[2]->IsUndefined(); +diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc +index fd29d17de195017970856ce30d7a9c5785b0b8ee..7fe3d09851d4476fa3f77ea0a0b49e8af13fae4f 100644 +--- a/src/crypto/crypto_x509.cc ++++ b/src/crypto/crypto_x509.cc +@@ -28,6 +28,7 @@ using v8::Array; + using v8::ArrayBuffer; + using v8::ArrayBufferView; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Boolean; + using v8::Context; + using v8::Date; +@@ -683,11 +684,8 @@ MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { + int size = i2d_RSA_PUBKEY(rsa, nullptr); + CHECK_GE(size, 0); + +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), size); +- } ++ auto bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), size, BackingStoreInitializationMode::kUninitialized); + + unsigned char* serialized = reinterpret_cast(bs->Data()); + CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); +diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc +index 5ace688bb7ffc86eedf5aff11ab0ab487ad9440e..31058543a164bb45ba989e8c433994e0857a98b9 100644 +--- a/src/encoding_binding.cc ++++ b/src/encoding_binding.cc +@@ -15,6 +15,7 @@ namespace encoding_binding { + + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Context; + using v8::FunctionCallbackInfo; + using v8::Isolate; +@@ -123,9 +124,8 @@ void BindingData::EncodeUtf8String(const FunctionCallbackInfo& args) { + + Local ab; + { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- std::unique_ptr bs = +- ArrayBuffer::NewBackingStore(isolate, length); ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ isolate, length, BackingStoreInitializationMode::kUninitialized); + + CHECK(bs); + +diff --git a/src/env-inl.h b/src/env-inl.h +index 98e1e1e75bae94038bba0049447ab48b0acfb8cc..fe395bf89f9c1e5bb2dabc8fceda7b9b2b877415 100644 +--- a/src/env-inl.h ++++ b/src/env-inl.h +@@ -44,16 +44,6 @@ + + namespace node { + +-NoArrayBufferZeroFillScope::NoArrayBufferZeroFillScope( +- IsolateData* isolate_data) +- : node_allocator_(isolate_data->node_allocator()) { +- if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 0; +-} +- +-NoArrayBufferZeroFillScope::~NoArrayBufferZeroFillScope() { +- if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 1; +-} +- + inline v8::Isolate* IsolateData::isolate() const { + return isolate_; + } +diff --git a/src/env.cc b/src/env.cc +index 161d577e0ea6a251c83ba1903b1ec9a582a5317c..52713421465c23c959afac1955168294b2048341 100644 +--- a/src/env.cc ++++ b/src/env.cc +@@ -39,6 +39,9 @@ namespace node { + + using errors::TryCatchScope; + using v8::Array; ++using v8::ArrayBuffer; ++using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Boolean; + using v8::Context; + using v8::CppHeap; +@@ -724,9 +727,10 @@ void Environment::add_refs(int64_t diff) { + } + + uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) { +- NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data()); +- std::unique_ptr bs = +- v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size); ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ isolate(), ++ suggested_size, ++ BackingStoreInitializationMode::kUninitialized); + uv_buf_t buf = uv_buf_init(static_cast(bs->Data()), bs->ByteLength()); + released_allocated_buffers_.emplace(buf.base, std::move(bs)); + return buf; +diff --git a/src/env.h b/src/env.h +index c346e3a9c827993036438685d758a734f9ce8c05..28c8df87c8e2f06e2ed8c554260bfdedb860bb4a 100644 +--- a/src/env.h ++++ b/src/env.h +@@ -114,19 +114,6 @@ class ModuleWrap; + class Environment; + class Realm; + +-// Disables zero-filling for ArrayBuffer allocations in this scope. This is +-// similar to how we implement Buffer.allocUnsafe() in JS land. +-class NoArrayBufferZeroFillScope { +- public: +- inline explicit NoArrayBufferZeroFillScope(IsolateData* isolate_data); +- inline ~NoArrayBufferZeroFillScope(); +- +- private: +- NodeArrayBufferAllocator* node_allocator_; +- +- friend class Environment; +-}; +- + struct IsolateDataSerializeInfo { + std::vector primitive_values; + std::vector template_values; +diff --git a/src/node_buffer.cc b/src/node_buffer.cc +index e844fe6cb33acefd075516e675075421ad5c3cff..06c84eb6ec097e3cb39502116135a7802aed13ce 100644 +--- a/src/node_buffer.cc ++++ b/src/node_buffer.cc +@@ -66,6 +66,7 @@ namespace Buffer { + using v8::ArrayBuffer; + using v8::ArrayBufferView; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Context; + using v8::EscapableHandleScope; + using v8::FunctionCallbackInfo; +@@ -376,9 +377,8 @@ MaybeLocal New(Environment* env, size_t length) { + + Local ab; + { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- std::unique_ptr bs = +- ArrayBuffer::NewBackingStore(isolate, length); ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ isolate, length, BackingStoreInitializationMode::kUninitialized); + + CHECK(bs); + +@@ -417,18 +417,14 @@ MaybeLocal Copy(Environment* env, const char* data, size_t length) { + return Local(); + } + +- Local ab; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- std::unique_ptr bs = +- ArrayBuffer::NewBackingStore(isolate, length); ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ isolate, length, BackingStoreInitializationMode::kUninitialized); + +- CHECK(bs); ++ CHECK(bs); + +- memcpy(bs->Data(), data, length); ++ memcpy(bs->Data(), data, length); + +- ab = ArrayBuffer::New(isolate, std::move(bs)); +- } ++ Local ab = ArrayBuffer::New(isolate, std::move(bs)); + + MaybeLocal obj = + New(env, ab, 0, ab->ByteLength()) +@@ -1440,14 +1436,16 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo& args) { + + Local buf; + +- NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator(); + // 0-length, or zero-fill flag is set, or building snapshot + if (size == 0 || per_process::cli_options->zero_fill_all_buffers || +- allocator == nullptr) { ++ env->isolate_data()->is_building_snapshot()) { + buf = ArrayBuffer::New(isolate, size); + } else { +- std::unique_ptr store = +- ArrayBuffer::NewBackingStore(isolate, size); ++ std::unique_ptr store = ArrayBuffer::NewBackingStore( ++ isolate, ++ size, ++ BackingStoreInitializationMode::kUninitialized, ++ v8::BackingStoreOnFailureMode::kReturnNull); + if (!store) { + return env->ThrowRangeError("Array buffer allocation failed"); + } +diff --git a/src/node_http2.cc b/src/node_http2.cc +index 8237c9b7d325dd925ae8798d7795fcd94eeb13d0..a22cf6c4e33e5cf2d3168ce03dc35af8a9584af7 100644 +--- a/src/node_http2.cc ++++ b/src/node_http2.cc +@@ -27,6 +27,7 @@ using v8::Array; + using v8::ArrayBuffer; + using v8::ArrayBufferView; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::Boolean; + using v8::Context; + using v8::EscapableHandleScope; +@@ -298,11 +299,10 @@ Local Http2Settings::Pack( + size_t count, + const nghttp2_settings_entry* entries) { + EscapableHandleScope scope(env->isolate()); +- std::unique_ptr bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(env->isolate(), count * 6); +- } ++ std::unique_ptr bs = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ count * 6, ++ BackingStoreInitializationMode::kUninitialized); + if (nghttp2_pack_settings_payload(static_cast(bs->Data()), + bs->ByteLength(), + entries, +@@ -468,13 +468,11 @@ Origins::Origins( + return; + } + +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs_ = ArrayBuffer::NewBackingStore(env->isolate(), +- alignof(nghttp2_origin_entry) - 1 + +- count_ * sizeof(nghttp2_origin_entry) + +- origin_string_len); +- } ++ bs_ = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ alignof(nghttp2_origin_entry) - 1 + ++ count_ * sizeof(nghttp2_origin_entry) + origin_string_len, ++ BackingStoreInitializationMode::kUninitialized); + + // Make sure the start address is aligned appropriately for an nghttp2_nv*. + char* start = nbytes::AlignUp(static_cast(bs_->Data()), +@@ -2120,12 +2118,10 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { + // happen, we concatenate the data we received with the already-stored + // pending input data, slicing off the already processed part. + size_t pending_len = stream_buf_.len - stream_buf_offset_; +- std::unique_ptr new_bs; +- { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); +- new_bs = ArrayBuffer::NewBackingStore(env()->isolate(), +- pending_len + nread); +- } ++ std::unique_ptr new_bs = ArrayBuffer::NewBackingStore( ++ env()->isolate(), ++ pending_len + nread, ++ BackingStoreInitializationMode::kUninitialized); + memcpy(static_cast(new_bs->Data()), + stream_buf_.base + stream_buf_offset_, + pending_len); +diff --git a/src/node_internals.h b/src/node_internals.h +index 12ea72b61b0a5e194207bb369dfed4b8667107cb..18844e18a32d6b07e62481138fa2342765643484 100644 +--- a/src/node_internals.h ++++ b/src/node_internals.h +@@ -121,8 +121,6 @@ v8::MaybeLocal InitializePrivateSymbols( + + class NodeArrayBufferAllocator : public ArrayBufferAllocator { + public: +- inline uint32_t* zero_fill_field() { return &zero_fill_field_; } +- + void* Allocate(size_t size) override; // Defined in src/node.cc + void* AllocateUninitialized(size_t size) override; + void Free(void* data, size_t size) override; +@@ -139,7 +137,6 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator { + } + + private: +- uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land. + std::atomic total_mem_usage_ {0}; + + // Delegate to V8's allocator for compatibility with the V8 memory cage. +diff --git a/src/stream_base.cc b/src/stream_base.cc +index fc81108120f0066f2d5dabedc74e22cb6c84d8e4..0bf2642599ee91e2d2aa20d6d066c80b3026ecc5 100644 +--- a/src/stream_base.cc ++++ b/src/stream_base.cc +@@ -19,6 +19,7 @@ namespace node { + using v8::Array; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; + using v8::ConstructorBehavior; + using v8::Context; + using v8::DontDelete; +@@ -243,8 +244,8 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { + + std::unique_ptr bs; + if (storage_size > 0) { +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(isolate, storage_size); ++ bs = ArrayBuffer::NewBackingStore( ++ isolate, storage_size, BackingStoreInitializationMode::kUninitialized); + } + + offset = 0; +@@ -398,14 +399,14 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { + + if (try_write) { + // Copy partial data +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(isolate, buf.len); ++ bs = ArrayBuffer::NewBackingStore( ++ isolate, buf.len, BackingStoreInitializationMode::kUninitialized); + memcpy(bs->Data(), buf.base, buf.len); + data_size = buf.len; + } else { + // Write it +- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); +- bs = ArrayBuffer::NewBackingStore(isolate, storage_size); ++ bs = ArrayBuffer::NewBackingStore( ++ isolate, storage_size, BackingStoreInitializationMode::kUninitialized); + data_size = StringBytes::Write(isolate, + static_cast(bs->Data()), + storage_size, diff --git a/patches/node/src_prepare_for_v8_sandboxing.patch b/patches/node/src_prepare_for_v8_sandboxing.patch new file mode 100644 index 0000000000..09f9b18afc --- /dev/null +++ b/patches/node/src_prepare_for_v8_sandboxing.patch @@ -0,0 +1,276 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: James M Snell +Date: Sun, 18 May 2025 10:46:30 -0700 +Subject: src: prepare for v8 sandboxing + +PR-URL: https://github.com/nodejs/node/pull/58376 +Reviewed-By: Yagiz Nizipli +Reviewed-By: Chengzhong Wu + +diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc +index f23cedf4f2449d8edc9a8de1b70332e75d693cdd..5ac2b1a83688fe99b13c37bf375ca6e22497dc18 100644 +--- a/src/crypto/crypto_dh.cc ++++ b/src/crypto/crypto_dh.cc +@@ -22,6 +22,8 @@ using ncrypto::DHPointer; + using ncrypto::EVPKeyCtxPointer; + using ncrypto::EVPKeyPointer; + using v8::ArrayBuffer; ++using v8::BackingStoreInitializationMode; ++using v8::BackingStoreOnFailureMode; + using v8::ConstructorBehavior; + using v8::Context; + using v8::DontDelete; +@@ -55,12 +57,27 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const { + + namespace { + MaybeLocal DataPointerToBuffer(Environment* env, DataPointer&& data) { ++#ifdef V8_ENABLE_SANDBOX ++ auto backing = ArrayBuffer::NewBackingStore( ++ env->isolate(), ++ data.size(), ++ BackingStoreInitializationMode::kUninitialized, ++ BackingStoreOnFailureMode::kReturnNull); ++ if (!backing) { ++ THROW_ERR_MEMORY_ALLOCATION_FAILED(env); ++ return MaybeLocal(); ++ } ++ if (data.size() > 0) { ++ memcpy(backing->Data(), data.get(), data.size()); ++ } ++#else + auto backing = ArrayBuffer::NewBackingStore( + data.get(), + data.size(), + [](void* data, size_t len, void* ptr) { DataPointer free_me(data, len); }, + nullptr); + data.release(); ++#endif // V8_ENABLE_SANDBOX + + auto ab = ArrayBuffer::New(env->isolate(), std::move(backing)); + return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); +diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc +index eab18ab9888e2f7c0757fefab80505d8c99dc742..7ecf810ea0f4106c7c44593dae1b0a3cf0673380 100644 +--- a/src/crypto/crypto_util.cc ++++ b/src/crypto/crypto_util.cc +@@ -37,6 +37,8 @@ using ncrypto::SSLCtxPointer; + using ncrypto::SSLPointer; + using v8::ArrayBuffer; + using v8::BackingStore; ++using v8::BackingStoreInitializationMode; ++using v8::BackingStoreOnFailureMode; + using v8::BigInt; + using v8::Context; + using v8::Exception; +@@ -359,34 +361,29 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept { + return *this; + } + +-std::unique_ptr ByteSource::ReleaseToBackingStore(Environment* env) { ++std::unique_ptr ByteSource::ReleaseToBackingStore( ++ Environment* env) { + // It's ok for allocated_data_ to be nullptr but + // only if size_ is zero. + CHECK_IMPLIES(size_ > 0, allocated_data_ != nullptr); +-#if defined(V8_ENABLE_SANDBOX) +- // When V8 sandboxed pointers are enabled, we have to copy into the memory +- // cage. We still want to ensure we erase the data on free though, so +- // provide a custom deleter that calls OPENSSL_cleanse. +- if (!size()) +- return ArrayBuffer::NewBackingStore(env->isolate(), 0); +- std::unique_ptr allocator(ArrayBuffer::Allocator::NewDefaultAllocator()); +- void* v8_data = allocator->Allocate(size()); +- CHECK(v8_data); +- memcpy(v8_data, allocated_data_, size()); +- OPENSSL_clear_free(allocated_data_, size()); ++#ifdef V8_ENABLE_SANDBOX ++ // If the v8 sandbox is enabled, then all array buffers must be allocated ++ // via the isolate. External buffers are not allowed. So, instead of wrapping ++ // the allocated data we'll copy it instead. ++ ++ // TODO(@jasnell): It would be nice to use an abstracted utility to do this ++ // branch instead of duplicating the V8_ENABLE_SANDBOX check each time. + std::unique_ptr ptr = ArrayBuffer::NewBackingStore( +- v8_data, ++ env->isolate(), + size(), +- [](void* data, size_t length, void*) { +- OPENSSL_cleanse(data, length); +- std::unique_ptr allocator(ArrayBuffer::Allocator::NewDefaultAllocator()); +- allocator->Free(data, length); +- }, nullptr); +- CHECK(ptr); +- allocated_data_ = nullptr; +- data_ = nullptr; +- size_ = 0; +- return ptr; ++ BackingStoreInitializationMode::kUninitialized, ++ BackingStoreOnFailureMode::kReturnNull); ++ if (!ptr) { ++ THROW_ERR_MEMORY_ALLOCATION_FAILED(env); ++ return nullptr; ++ } ++ memcpy(ptr->Data(), allocated_data_, size()); ++ OPENSSL_clear_free(allocated_data_, size_); + #else + std::unique_ptr ptr = ArrayBuffer::NewBackingStore( + allocated_data_, +@@ -394,12 +391,12 @@ std::unique_ptr ByteSource::ReleaseToBackingStore(Environment* env + [](void* data, size_t length, void* deleter_data) { + OPENSSL_clear_free(deleter_data, length); + }, allocated_data_); ++#endif // V8_ENABLE_SANDBOX + CHECK(ptr); + allocated_data_ = nullptr; + data_ = nullptr; + size_ = 0; + return ptr; +-#endif // defined(V8_ENABLE_SANDBOX) + } + + Local ByteSource::ToArrayBuffer(Environment* env) { +@@ -711,8 +708,19 @@ void SecureBuffer(const FunctionCallbackInfo& args) { + } + #else + void SecureBuffer(const FunctionCallbackInfo& args) { +- CHECK(args[0]->IsUint32()); + Environment* env = Environment::GetCurrent(args); ++#ifdef V8_ENABLE_SANDBOX ++ // The v8 sandbox is enabled, so we cannot use the secure heap because ++ // the sandbox requires that all array buffers be allocated via the isolate. ++ // That is fundamentally incompatible with the secure heap which allocates ++ // in openssl's secure heap area. Instead we'll just throw an error here. ++ // ++ // That said, we really shouldn't get here in the first place since the ++ // option to enable the secure heap is only available when the sandbox ++ // is disabled. ++ UNREACHABLE(); ++#else ++ CHECK(args[0]->IsUint32()); + uint32_t len = args[0].As()->Value(); + void* data = OPENSSL_malloc(len); + if (data == nullptr) { +@@ -730,6 +738,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { + data); + Local buffer = ArrayBuffer::New(env->isolate(), store); + args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len)); ++#endif // V8_ENABLE_SANDBOX + } + #endif // defined(V8_ENABLE_SANDBOX) + +diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc +index 7fe3d09851d4476fa3f77ea0a0b49e8af13fae4f..b60ad6b1cdb0b923ba2de1b7205ed5ce07612b81 100644 +--- a/src/crypto/crypto_x509.cc ++++ b/src/crypto/crypto_x509.cc +@@ -29,6 +29,7 @@ using v8::ArrayBuffer; + using v8::ArrayBufferView; + using v8::BackingStore; + using v8::BackingStoreInitializationMode; ++using v8::BackingStoreOnFailureMode; + using v8::Boolean; + using v8::Context; + using v8::Date; +@@ -168,18 +169,20 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { + MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { + if (bio == nullptr || !*bio) return {}; + BUF_MEM* mem = *bio; +-#if defined(V8_ENABLE_SANDBOX) +- std::unique_ptr allocator(ArrayBuffer::Allocator::NewDefaultAllocator()); +- void* v8_data = allocator->Allocate(mem->length); +- CHECK(v8_data); +- memcpy(v8_data, mem->data, mem->length); +- std::unique_ptr backing = ArrayBuffer::NewBackingStore( +- v8_data, ++#ifdef V8_ENABLE_SANDBOX ++ // If the v8 sandbox is enabled, then all array buffers must be allocated ++ // via the isolate. External buffers are not allowed. So, instead of wrapping ++ // the BIOPointer we'll copy it instead. ++ auto backing = ArrayBuffer::NewBackingStore( ++ env->isolate(), + mem->length, +- [](void* data, size_t length, void*) { +- std::unique_ptr allocator(ArrayBuffer::Allocator::NewDefaultAllocator()); +- allocator->Free(data, length); +- }, nullptr); ++ BackingStoreInitializationMode::kUninitialized, ++ BackingStoreOnFailureMode::kReturnNull); ++ if (!backing) { ++ THROW_ERR_MEMORY_ALLOCATION_FAILED(env); ++ return MaybeLocal(); ++ } ++ memcpy(backing->Data(), mem->data, mem->length); + #else + auto backing = ArrayBuffer::NewBackingStore( + mem->data, +@@ -188,8 +191,7 @@ MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { + BIOPointer free_me(static_cast(data)); + }, + bio->release()); +-#endif +- ++#endif // V8_ENABLE_SANDBOX + auto ab = ArrayBuffer::New(env->isolate(), std::move(backing)); + Local ret; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {}; +diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc +index 413db3ed9b88d7b7fb2ac6dd1153dade9ff830fd..6da93b8569a34841e846c320ec0a6ca7f1ea0da6 100644 +--- a/src/js_native_api_v8.cc ++++ b/src/js_native_api_v8.cc +@@ -114,7 +114,7 @@ napi_status NewExternalString(napi_env env, + CHECK_NEW_STRING_ARGS(env, str, length, result); + + napi_status status; +-#if defined(V8_ENABLE_SANDBOX) ++#ifdef V8_ENABLE_SANDBOX + status = create_api(env, str, length, result); + if (status == napi_ok) { + if (copied != nullptr) { +diff --git a/src/node_api.cc b/src/node_api.cc +index 2769997f0ede0e921fcb8826942609e497e5f9cb..d9b17780f6143f1c3f8488a20144376963e43fbc 100644 +--- a/src/node_api.cc ++++ b/src/node_api.cc +@@ -1056,7 +1056,7 @@ napi_create_external_buffer(napi_env env, + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + +-#if defined(V8_ENABLE_SANDBOX) ++#ifdef V8_ENABLE_SANDBOX + return napi_set_last_error(env, napi_no_external_buffers_allowed); + #else + v8::Isolate* isolate = env->isolate; +diff --git a/src/node_options.cc b/src/node_options.cc +index e93e8684e518b30a2514768a269be6d32d1f5b94..547cda780376f578b0f78eb9158dc14a3faf874d 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -83,6 +83,8 @@ void PerProcessOptions::CheckOptions(std::vector* errors, + } + + // Any value less than 2 disables use of the secure heap. ++#ifndef V8_ENABLE_SANDBOX ++ // The secure heap is not supported when V8_ENABLE_SANDBOX is enabled. + if (secure_heap >= 2) { + if ((secure_heap & (secure_heap - 1)) != 0) + errors->push_back("--secure-heap must be a power of 2"); +@@ -95,6 +97,7 @@ void PerProcessOptions::CheckOptions(std::vector* errors, + if ((secure_heap_min & (secure_heap_min - 1)) != 0) + errors->push_back("--secure-heap-min must be a power of 2"); + } ++#endif // V8_ENABLE_SANDBOX + #endif // HAVE_OPENSSL + + if (use_largepages != "off" && +@@ -1243,6 +1246,7 @@ PerProcessOptionsParser::PerProcessOptionsParser( + "force FIPS crypto (cannot be disabled)", + &PerProcessOptions::force_fips_crypto, + kAllowedInEnvvar); ++#ifndef V8_ENABLE_SANDBOX + AddOption("--secure-heap", + "total size of the OpenSSL secure heap", + &PerProcessOptions::secure_heap, +@@ -1251,6 +1255,7 @@ PerProcessOptionsParser::PerProcessOptionsParser( + "minimum allocation size from the OpenSSL secure heap", + &PerProcessOptions::secure_heap_min, + kAllowedInEnvvar); ++#endif // V8_ENABLE_SANDBOX + #endif // HAVE_OPENSSL + #if OPENSSL_VERSION_MAJOR >= 3 + AddOption("--openssl-legacy-provider",