chore: bump node to v22.22.0 (38-x-y) (#49388)

* chore: bump node in DEPS to v22.22.0

* chore: update patches

* chore: fixup sandboxed pointers patch

(cherry picked from commit f52fbdbe51)

* tls: route callback exceptions through error handlers

https://github.com/nodejs-private/node-private/pull/782
(cherry picked from commit 87bc8ebd34)
(cherry picked from commit 2b6f185521)

* chore:remove zero-fill sandbox patch component

xref https://github.com/electron/electron/pull/49452

(cherry picked from commit bdb87f9dbb)

* fixup! chore:remove zero-fill sandbox patch component

(cherry picked from commit 6a4e4e3821)
Co-Authored-By: Robo <hop2deep@gmail.com>

* test: correct conditional secure heap flags test

xref:  https://github.com/nodejs/node/pull/60385
(cherry picked from commit 1304ff2d83)

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
This commit is contained in:
electron-roller[bot]
2026-01-23 10:48:43 -05:00
committed by GitHub
parent e342216d9e
commit c8af46e054
11 changed files with 1003 additions and 96 deletions

2
DEPS
View File

@@ -4,7 +4,7 @@ vars = {
'chromium_version':
'140.0.7339.249',
'node_version':
'v22.21.1',
'v22.22.0',
'nan_version':
'e14bdcd1f72d62bca1d541b66da43130384ec213',
'squirrel.mac_version':

View File

@@ -47,3 +47,6 @@ fix_array_out-of-bounds_read_in_boyer-moore_search.patch
chore_add_missing_include_of_iterator.patch
src_use_cp_utf8_for_wide_file_names_on_win32.patch
fix_ensure_traverseparent_bails_on_resource_path_exit.patch
remove_obsolete_noarraybufferzerofillscope.patch
src_prepare_for_v8_sandboxing.patch
test_correct_conditional_secure_heap_flags_test.patch

View File

@@ -8,10 +8,10 @@ they use themselves as the entry point. We should try to upstream some form
of this.
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index d12d21905c4823f45cffeea4423e99e81b1008f5..4987cd3f6c9eefb440bca3f58113df6cd5b410ac 100644
index 15443a710ccf53fae333da3b1fbb52a970c658d5..464b34829c1a566836bfca6bbc2b87fcf5e50016 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -267,12 +267,14 @@ function patchProcessObject(expandArgv1) {
@@ -265,12 +265,14 @@ function patchProcessObject(expandArgv1) {
// the entry point.
if (expandArgv1 && process.argv[1] && process.argv[1][0] !== '-') {
// Expand process.argv[1] into a full path.

View File

@@ -86,10 +86,10 @@ index 0ca643aa74d13f278685d2330b791182b55c15b4..cbcecfba33070b820aca0e2814982160
NODE_DEFINE_CONSTANT(target, ETIMEDOUT);
#endif
diff --git a/src/node_errors.cc b/src/node_errors.cc
index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c47202561619b164 100644
index 238942d45a136facec55ca5a2534e2dc407137e9..36a21b9523351fe2f225ffe7fca184d737640b62 100644
--- a/src/node_errors.cc
+++ b/src/node_errors.cc
@@ -862,10 +862,6 @@ const char* errno_string(int errorno) {
@@ -899,10 +899,6 @@ const char* errno_string(int errorno) {
ERRNO_CASE(ENOBUFS);
#endif
@@ -100,7 +100,7 @@ index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c4720256
#ifdef ENODEV
ERRNO_CASE(ENODEV);
#endif
@@ -904,14 +900,6 @@ const char* errno_string(int errorno) {
@@ -941,14 +937,6 @@ const char* errno_string(int errorno) {
ERRNO_CASE(ENOSPC);
#endif
@@ -115,7 +115,7 @@ index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c4720256
#ifdef ENOSYS
ERRNO_CASE(ENOSYS);
#endif
@@ -994,10 +982,6 @@ const char* errno_string(int errorno) {
@@ -1031,10 +1019,6 @@ const char* errno_string(int errorno) {
ERRNO_CASE(ESTALE);
#endif

View File

@@ -48,7 +48,7 @@ index fe669d40c31a29334b047b9cfee3067f64ef0a7b..9e5de7bbe574add017cd12ee091304d0
static CFunction fast_timing_safe_equal(CFunction::Make(FastTimingSafeEqual));
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344e563ad72 100644
index b9f0c97938203b4652780a7d707c5e83319330b0..8a5b6b57321c2843a965a7e51b2ebed991a1e424 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -44,6 +44,14 @@
@@ -74,7 +74,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
using v8::FunctionCallbackInfo;
using v8::Global;
using v8::HandleScope;
@@ -584,19 +591,24 @@ void SlowCopy(const FunctionCallbackInfo<Value>& args) {
@@ -583,19 +590,24 @@ void SlowCopy(const FunctionCallbackInfo<Value>& args) {
// Assume caller has properly validated args.
uint32_t FastCopy(Local<Value> receiver,
@@ -107,7 +107,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
return to_copy;
}
@@ -865,19 +877,17 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
@@ -864,19 +876,17 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
}
int32_t FastCompare(v8::Local<v8::Value>,
@@ -135,7 +135,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
}
static v8::CFunction fast_compare(v8::CFunction::Make(FastCompare));
@@ -1149,14 +1159,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
@@ -1148,14 +1158,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
}
int32_t FastIndexOfNumber(v8::Local<v8::Value>,
@@ -153,7 +153,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
}
static v8::CFunction fast_index_of_number(
@@ -1496,21 +1505,31 @@ void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
@@ -1510,21 +1519,31 @@ void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
template <encoding encoding>
uint32_t FastWriteString(Local<Value> receiver,

View File

@@ -18,10 +18,10 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs
from the above issue.
diff --git a/src/api/environment.cc b/src/api/environment.cc
index cb37fa080fc8e8d524cfa2758c4a8c2c5652324d..8e227ddd1be50c046a8cf2895a31d607eb7d31de 100644
index fd71ceac65ccef1d2832b45b0b5612877cee22c1..ceac508418f489a8077c1bc85a2feaf85bf60480 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -316,6 +316,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
@@ -308,6 +308,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
MultiIsolatePlatform* platform,
const SnapshotData* snapshot_data,
const IsolateSettings& settings) {
@@ -32,7 +32,7 @@ index cb37fa080fc8e8d524cfa2758c4a8c2c5652324d..8e227ddd1be50c046a8cf2895a31d607
Isolate* isolate = Isolate::Allocate();
if (isolate == nullptr) return nullptr;
@@ -359,9 +363,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
@@ -351,9 +355,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
const EmbedderSnapshotData* snapshot_data,

View File

@@ -0,0 +1,653 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Robo <hop2deep@gmail.com>
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 ceac508418f489a8077c1bc85a2feaf85bf60480..645c4cbc0fcf9ec004dcb55493104796b0d64de2 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -107,11 +107,7 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> 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<const unsigned char*>(data),
@@ -852,11 +853,10 @@ bool CipherBase::Final(std::unique_ptr<BackingStore>* out) {
const int mode = ctx_.getMode();
- {
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
- *out = ArrayBuffer::NewBackingStore(
- env()->isolate(), static_cast<size_t>(ctx_.getBlockSize()));
- }
+ *out = ArrayBuffer::NewBackingStore(
+ env()->isolate(),
+ static_cast<size_t>(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<Object> ECPointToBuffer(Environment* env,
return MaybeLocal<Object>();
}
- std::unique_ptr<BackingStore> bs;
- {
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
- bs = ArrayBuffer::NewBackingStore(env->isolate(), len);
- }
+ std::unique_ptr<BackingStore> 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<Value>& args) {
return;
}
- std::unique_ptr<BackingStore> 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<Value>& args) {
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
"Failed to get ECDH private key");
- std::unique_ptr<BackingStore> 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<unsigned char*>(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<void> GetRsaKeyDetail(Environment* env,
return Nothing<void>();
}
- std::unique_ptr<BackingStore> 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<unsigned char*>(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<BackingStore> Node_SignFinal(Environment* env,
return nullptr;
size_t sig_len = pkey.size();
- std::unique_ptr<BackingStore> 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<BackingStore> ConvertSignatureToP1363(
if (n == kNoDsaSignature)
return std::move(signature);
- std::unique_ptr<BackingStore> 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<unsigned char*>(signature->Data()),
static_cast<unsigned char*>(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<char*>(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<Value>& args) {
if (len == 0)
return;
- std::unique_ptr<BackingStore> 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<Value>& args) {
if (len == 0)
return;
- std::unique_ptr<BackingStore> 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<Value>& args) {
if (slen <= 0)
return; // Invalid or malformed session.
- std::unique_ptr<BackingStore> 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<unsigned char*>(bs->Data());
CHECK_LT(0, i2d_SSL_SESSION(sess, &p));
@@ -1993,11 +1987,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo<Value>& args) {
uint32_t olen = args[0].As<Uint32>()->Value();
Utf8Value label(env->isolate(), args[1]);
- std::unique_ptr<BackingStore> 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 eb6dad44a49d997097c8fb5009eeb60a7305da27..0a1a3c694f3f544ada62235ade6a03a9deadfede 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<Object> GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) {
int size = i2d_RSA_PUBKEY(rsa, nullptr);
CHECK_GE(size, 0);
- std::unique_ptr<BackingStore> 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<unsigned char*>(bs->Data());
CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0);
diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc
index 31ed995714bb99ab534f26ba9ebc6051c258a1c9..9bdb2a660364c66f3f141b505225dcf35c5bbc65 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<Value>& args) {
Local<ArrayBuffer> ab;
{
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
- std::unique_ptr<BackingStore> bs =
- ArrayBuffer::NewBackingStore(isolate, length);
+ std::unique_ptr<BackingStore> 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 5fa667382bc957aee800d612f78b18c37a58c67f..49a85e9a23e50f201d0b93d8b205e104c0f0fe2c 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<v8::BackingStore> bs =
- v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size);
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
+ isolate(),
+ suggested_size,
+ BackingStoreInitializationMode::kUninitialized);
uv_buf_t buf = uv_buf_init(static_cast<char*>(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<SnapshotIndex> primitive_values;
std::vector<PropInfo> template_values;
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index 8a5b6b57321c2843a965a7e51b2ebed991a1e424..d7bd35a1025487d7a810b14cd5d3f2ba74cc2378 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;
@@ -375,9 +376,8 @@ MaybeLocal<Object> New(Environment* env, size_t length) {
Local<ArrayBuffer> ab;
{
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
- std::unique_ptr<BackingStore> bs =
- ArrayBuffer::NewBackingStore(isolate, length);
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
+ isolate, length, BackingStoreInitializationMode::kUninitialized);
CHECK(bs);
@@ -416,18 +416,14 @@ MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
return Local<Object>();
}
- Local<ArrayBuffer> ab;
- {
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
- std::unique_ptr<BackingStore> bs =
- ArrayBuffer::NewBackingStore(isolate, length);
+ std::unique_ptr<BackingStore> 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<ArrayBuffer> ab = ArrayBuffer::New(isolate, std::move(bs));
MaybeLocal<Object> obj =
New(env, ab, 0, ab->ByteLength())
@@ -1439,14 +1435,16 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
Local<ArrayBuffer> 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<BackingStore> store =
- ArrayBuffer::NewBackingStore(isolate, size);
+ std::unique_ptr<BackingStore> 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<Value> Http2Settings::Pack(
size_t count,
const nghttp2_settings_entry* entries) {
EscapableHandleScope scope(env->isolate());
- std::unique_ptr<BackingStore> bs;
- {
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
- bs = ArrayBuffer::NewBackingStore(env->isolate(), count * 6);
- }
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
+ env->isolate(),
+ count * 6,
+ BackingStoreInitializationMode::kUninitialized);
if (nghttp2_pack_settings_payload(static_cast<uint8_t*>(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<char*>(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<BackingStore> new_bs;
- {
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
- new_bs = ArrayBuffer::NewBackingStore(env()->isolate(),
- pending_len + nread);
- }
+ std::unique_ptr<BackingStore> new_bs = ArrayBuffer::NewBackingStore(
+ env()->isolate(),
+ pending_len + nread,
+ BackingStoreInitializationMode::kUninitialized);
memcpy(static_cast<char*>(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<v8::Object> 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<size_t> 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<Value>& args) {
std::unique_ptr<BackingStore> 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<Value>& 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<char*>(bs->Data()),
storage_size,

View File

@@ -0,0 +1,276 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: James M Snell <jasnell@gmail.com>
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 <yagiz@nizipli.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
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<Value> 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<Value>();
+ }
+ 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<Value>());
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<BackingStore> ByteSource::ReleaseToBackingStore(Environment* env) {
+std::unique_ptr<BackingStore> 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<ArrayBuffer::Allocator> 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<BackingStore> ptr = ArrayBuffer::NewBackingStore(
- v8_data,
+ env->isolate(),
size(),
- [](void* data, size_t length, void*) {
- OPENSSL_cleanse(data, length);
- std::unique_ptr<ArrayBuffer::Allocator> 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<BackingStore> ptr = ArrayBuffer::NewBackingStore(
allocated_data_,
@@ -394,12 +391,12 @@ std::unique_ptr<BackingStore> 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<ArrayBuffer> ByteSource::ToArrayBuffer(Environment* env) {
@@ -711,8 +708,19 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
}
#else
void SecureBuffer(const FunctionCallbackInfo<Value>& 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<Uint32>()->Value();
void* data = OPENSSL_malloc(len);
if (data == nullptr) {
@@ -730,6 +738,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
data);
Local<ArrayBuffer> 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 0a1a3c694f3f544ada62235ade6a03a9deadfede..0ac2379c0899f1080dd325d492496555c5e1c6af 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<Value> ToV8Value(Local<Context> context, const BIOPointer& bio) {
MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
if (bio == nullptr || !*bio) return {};
BUF_MEM* mem = *bio;
-#if defined(V8_ENABLE_SANDBOX)
- std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
- void* v8_data = allocator->Allocate(mem->length);
- CHECK(v8_data);
- memcpy(v8_data, mem->data, mem->length);
- std::unique_ptr<v8::BackingStore> 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<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
- allocator->Free(data, length);
- }, nullptr);
+ BackingStoreInitializationMode::kUninitialized,
+ BackingStoreOnFailureMode::kReturnNull);
+ if (!backing) {
+ THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
+ return MaybeLocal<Value>();
+ }
+ memcpy(backing->Data(), mem->data, mem->length);
#else
auto backing = ArrayBuffer::NewBackingStore(
mem->data,
@@ -188,8 +191,7 @@ MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
BIOPointer free_me(static_cast<BIO*>(data));
},
bio->release());
-#endif
-
+#endif // V8_ENABLE_SANDBOX
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
Local<Value> 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 7b2efa49468c0bed2f5935552addd3ab37d0a50b..97eb62047b6692d63deacf2e7346e85351337e85 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 b067685822dc056e446e1a9402a5a6cba86cc722..cf70816d3ebacee1aaec6d465e6ebdc5f1dec5c3 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -83,6 +83,8 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* 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<std::string>* 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",

View File

@@ -6,63 +6,6 @@ Subject: support V8 sandboxed pointers
This refactors several allocators to allocate within the V8 memory cage,
allowing them to be compatible with the V8_SANDBOXED_POINTERS feature.
diff --git a/src/api/environment.cc b/src/api/environment.cc
index fd71ceac65ccef1d2832b45b0b5612877cee22c1..cb37fa080fc8e8d524cfa2758c4a8c2c5652324d 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -106,6 +106,14 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
return result;
}
+NodeArrayBufferAllocator::NodeArrayBufferAllocator() {
+ zero_fill_field_ = static_cast<uint32_t*>(allocator_->Allocate(sizeof(*zero_fill_field_)));
+}
+
+NodeArrayBufferAllocator::~NodeArrayBufferAllocator() {
+ allocator_->Free(zero_fill_field_, sizeof(*zero_fill_field_));
+}
+
void* NodeArrayBufferAllocator::Allocate(size_t size) {
void* ret;
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
index f23cedf4f2449d8edc9a8de1b70332e75d693cdd..976653dd1e9363e046788fc3419a9b649ceb2ea4 100644
--- a/src/crypto/crypto_dh.cc
+++ b/src/crypto/crypto_dh.cc
@@ -55,13 +55,32 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
namespace {
MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
+#if defined(V8_ENABLE_SANDBOX)
+ std::unique_ptr<v8::BackingStore> backing;
+ if (data.size() > 0) {
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
+ void* v8_data = allocator->Allocate(data.size());
+ CHECK(v8_data);
+ memcpy(v8_data, data.get(), data.size());
+ backing = ArrayBuffer::NewBackingStore(
+ v8_data,
+ data.size(),
+ [](void* data, size_t length, void*) {
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
+ allocator->Free(data, length);
+ }, nullptr);
+ } else {
+ NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
+ backing = v8::ArrayBuffer::NewBackingStore(env->isolate(), 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
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>());
}
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
index 4505786745c54a529f904d5e7813a86204e0a78b..eab18ab9888e2f7c0757fefab80505d8c99dc742 100644
--- a/src/crypto/crypto_util.cc
@@ -188,6 +131,28 @@ index f616223cfb0f6e10f7cf57ada9704316bde2797e..eb6dad44a49d997097c8fb5009eeb60a
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
Local<Value> ret;
if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {};
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index 357dc5f6d1c1c2d3756a94c1326b0502403e1eaf..b9f0c97938203b4652780a7d707c5e83319330b0 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -1412,7 +1412,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
CHECK(value >= 0 && value < maxSize);
size_t size = static_cast<size_t>(value);
#ifdef V8_ENABLE_SANDBOX
- CHECK_LE(size, kMaxSafeBufferSizeForSandbox);
+ CHECK_LE(size, v8::internal::kMaxSafeBufferSizeForSandbox);
#endif
return size;
}
@@ -1437,7 +1437,7 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
buf = ArrayBuffer::New(isolate, size);
} else {
std::unique_ptr<BackingStore> store =
- ArrayBuffer::NewBackingStoreForNodeLTS(isolate, size);
+ ArrayBuffer::NewBackingStore(isolate, size);
if (!store) {
return env->ThrowRangeError("Array buffer allocation failed");
}
diff --git a/src/node_i18n.cc b/src/node_i18n.cc
index 6be3920632b25db450025ebab6a2636e4811cdbe..b49916d2b5fc5e58cf3fb67329430fd3df8fb813 100644
--- a/src/node_i18n.cc
@@ -228,30 +193,6 @@ index 6be3920632b25db450025ebab6a2636e4811cdbe..b49916d2b5fc5e58cf3fb67329430fd3
}
constexpr const char* EncodingName(const enum encoding encoding) {
diff --git a/src/node_internals.h b/src/node_internals.h
index 12ea72b61b0a5e194207bb369dfed4b8667107cb..64442215714a98f648971e517ddd9c77e38fe3f2 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -121,7 +121,9 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
class NodeArrayBufferAllocator : public ArrayBufferAllocator {
public:
- inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
+ NodeArrayBufferAllocator();
+ ~NodeArrayBufferAllocator() override;
+ 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;
@@ -139,7 +141,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
}
private:
- uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
+ uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
std::atomic<size_t> total_mem_usage_ {0};
// Delegate to V8's allocator for compatibility with the V8 memory cage.
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
index c55a2e28066147ae5ca5def10ec76ccc03c634b4..c54183c72944989219b6437c9e571a3f7f3f8dd5 100644
--- a/src/node_serdes.cc

View File

@@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Tue, 4 Nov 2025 21:20:26 +0100
Subject: test: correct conditional secure heap flags test
PR-URL: https://github.com/nodejs/node/pull/60385
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
(cherry picked from commit 53c4a39fec941e04150554fdd3e654b48f2e1b31)
diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js
index afd43cfffe638f4f084f1c36949068e7239eadc3..c70e073bab888c349e8f5c691f5679a3796c896c 100644
--- a/test/parallel/test-process-env-allowed-flags-are-documented.js
+++ b/test/parallel/test-process-env-allowed-flags-are-documented.js
@@ -49,6 +49,8 @@ if (!hasOpenSSL3) {
documented.delete('--openssl-shared-config');
}
+const isV8Sandboxed = process.config.variables.v8_enable_sandbox;
+
// Filter out options that are conditionally present.
const conditionalOpts = [
{
@@ -74,6 +76,9 @@ const conditionalOpts = [
}, {
include: process.features.inspector,
filter: (opt) => opt.startsWith('--inspect') || opt === '--debug-port'
+ }, {
+ include: !isV8Sandboxed,
+ filter: (opt) => ['--secure-heap', '--secure-heap-min'].includes(opt)
},
];
documented.forEach((opt) => {

View File

@@ -113,6 +113,7 @@
"parallel/test-tls-passphrase",
"parallel/test-tls-peer-certificate",
"parallel/test-tls-pfx-authorizationerror",
"parallel/test-tls-psk-alpn-callback-exception-handling",
"parallel/test-tls-psk-circuit",
"parallel/test-tls-reduced-SECLEVEL-in-cipher",
"parallel/test-tls-root-certificates",