@@ -6,75 +6,6 @@ Subject: support V8 sandboxed pointers
66This refactors several allocators to allocate within the V8 memory cage,
77allowing them to be compatible with the V8_SANDBOXED_POINTERS feature.
88
9- diff --git a/src/api/environment.cc b/src/api/environment.cc
10- index 53f05293bd94e159dfedf48735989e668acdd08e..5c8bc870dcf2e974036cf3bcb60fd288e59045d9 100644
11- --- a/src/api/environment.cc
12- +++ b/src/api/environment.cc
13- @@ -111,6 +111,14 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
14- return result;
15- }
16-
17- + NodeArrayBufferAllocator::NodeArrayBufferAllocator() {
18- + zero_fill_field_ = static_cast<uint32_t*>(allocator_->Allocate(sizeof(*zero_fill_field_)));
19- + }
20- +
21- + NodeArrayBufferAllocator::~NodeArrayBufferAllocator() {
22- + allocator_->Free(zero_fill_field_, sizeof(*zero_fill_field_));
23- + }
24- +
25- void* NodeArrayBufferAllocator::Allocate(size_t size) {
26- void* ret;
27- COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled");
28- @@ -337,6 +345,12 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
29- // but also otherwise just doesn't work, and the only real alternative
30- // is disabling shared-readonly-heap mode altogether.
31- static Isolate::CreateParams first_params = *params;
32- + // Clear allocator pointers to prevent use-after-free during static
33- + // destruction. The static first_params can outlive V8's internal
34- + // allocator systems, causing crashes when its destructor tries to
35- + // free resources after V8 has shut down.
36- + first_params.array_buffer_allocator = nullptr;
37- + first_params.array_buffer_allocator_shared.reset();
38- params->snapshot_blob = first_params.snapshot_blob;
39- params->external_references = first_params.external_references;
40- }
41- diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
42- index 46a7d1396dc1a175ae99f4e403721f1730fdd320..bbb0abb3b9563074d350578e0f5a8fa211046b17 100644
43- --- a/src/crypto/crypto_dh.cc
44- +++ b/src/crypto/crypto_dh.cc
45- @@ -61,17 +61,22 @@ MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
46- bool secure;
47- };
48- #ifdef V8_ENABLE_SANDBOX
49- - auto backing = ArrayBuffer::NewBackingStore(
50- - env->isolate(),
51- - data.size(),
52- - BackingStoreInitializationMode::kUninitialized,
53- - BackingStoreOnFailureMode::kReturnNull);
54- - if (!backing) {
55- - THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
56- - return MaybeLocal<Value>();
57- - }
58- + std::unique_ptr<v8::BackingStore> backing;
59- if (data.size() > 0) {
60- - memcpy(backing->Data(), data.get(), data.size());
61- + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
62- + void* v8_data = allocator->Allocate(data.size());
63- + CHECK(v8_data);
64- + memcpy(v8_data, data.get(), data.size());
65- + backing = ArrayBuffer::NewBackingStore(
66- + v8_data,
67- + data.size(),
68- + [](void* data, size_t length, void*) {
69- + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
70- + allocator->Free(data, length);
71- + }, nullptr);
72- + } else {
73- + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
74- + backing = v8::ArrayBuffer::NewBackingStore(env->isolate(), data.size());
75- }
76- #else
77- auto backing = ArrayBuffer::NewBackingStore(
789diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
7910index 12b0d804c6f1d4998b85160b0aac8eb7a3b5576b..27bd93769233dc65a064710db4095d9cdc3a8b1a 100644
8011--- a/src/crypto/crypto_util.cc
@@ -189,110 +120,11 @@ index b30297eac08ad9587642b723f91d7e3b954294d4..4c5427596d1c90d3a413cdd9ff4f1151
189120 #else
190121 auto backing = ArrayBuffer::NewBackingStore(
191122 mem->data,
192- diff --git a/src/env-inl.h b/src/env-inl.h
193- index 97c43afb487b58c0c77bd59b4a6b6d7a13690053..5dfbd564d5bbd22ebf3b529a07b73e85cbe51986 100644
194- --- a/src/env-inl.h
195- +++ b/src/env-inl.h
196- @@ -44,6 +44,16 @@
197-
198- namespace node {
199-
200- + NoArrayBufferZeroFillScope::NoArrayBufferZeroFillScope(
201- + IsolateData* isolate_data)
202- + : node_allocator_(isolate_data->node_allocator()) {
203- + if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 0;
204- + }
205- +
206- + NoArrayBufferZeroFillScope::~NoArrayBufferZeroFillScope() {
207- + if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 1;
208- + }
209- +
210- inline v8::Isolate* IsolateData::isolate() const {
211- return isolate_;
212- }
213- diff --git a/src/env.h b/src/env.h
214- index 84a650885a79bc5c49efdc26f62ec8db48de775c..ba442937bf0d7831c9a84b5a57211e9f90c81705 100644
215- --- a/src/env.h
216- +++ b/src/env.h
217- @@ -111,6 +111,19 @@ class ModuleWrap;
218- class Environment;
219- class Realm;
220-
221- + // Disables zero-filling for ArrayBuffer allocations in this scope. This is
222- + // similar to how we implement Buffer.allocUnsafe() in JS land.
223- + class NoArrayBufferZeroFillScope {
224- + public:
225- + inline explicit NoArrayBufferZeroFillScope(IsolateData* isolate_data);
226- + inline ~NoArrayBufferZeroFillScope();
227- +
228- + private:
229- + NodeArrayBufferAllocator* node_allocator_;
230- +
231- + friend class Environment;
232- + };
233- +
234- struct IsolateDataSerializeInfo {
235- std::vector<SnapshotIndex> primitive_values;
236- std::vector<PropInfo> template_values;
237123diff --git a/src/node_buffer.cc b/src/node_buffer.cc
238- index 49df0b4284748effb27b05ecd69f05699dd8be75..eb104ab80fd2052d1523cb2f2ecdb6d23ac3be98 100644
124+ index 49df0b4284748effb27b05ecd69f05699dd8be75..77da37881bb351249c45e405289193c10d083e0c 100644
239125--- a/src/node_buffer.cc
240126+++ b/src/node_buffer.cc
241- @@ -81,6 +81,7 @@ using v8::SharedArrayBuffer;
242- using v8::String;
243- using v8::Uint32;
244- using v8::Uint8Array;
245- + using v8::Uint32Array;
246- using v8::Value;
247-
248- namespace {
249- @@ -1243,6 +1244,45 @@ void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
250- realm->set_buffer_prototype_object(proto);
251- }
252-
253- + void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) {
254- + Environment* env = Environment::GetCurrent(args);
255- + NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
256- + Local<ArrayBuffer> ab;
257- + // It can be a nullptr when running inside an isolate where we
258- + // do not own the ArrayBuffer allocator.
259- + if (allocator == nullptr || env->isolate_data()->is_building_snapshot()) {
260- + // Create a dummy Uint32Array - the JS land can only toggle the C++ land
261- + // setting when the allocator uses our toggle. With this the toggle in JS
262- + // land results in no-ops.
263- + // When building a snapshot, just use a dummy toggle as well to avoid
264- + // introducing the dynamic external reference. We'll re-initialize the
265- + // toggle with a real one connected to the C++ allocator after snapshot
266- + // deserialization.
267- +
268- + ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t));
269- + } else {
270- + // TODO(joyeecheung): save ab->GetBackingStore()->Data() in the Node.js
271- + // array buffer allocator and include it into the C++ toggle while the
272- + // Environment is still alive.
273- + uint32_t* zero_fill_field = allocator->zero_fill_field();
274- + std::unique_ptr<BackingStore> backing =
275- + ArrayBuffer::NewBackingStore(zero_fill_field,
276- + sizeof(*zero_fill_field),
277- + [](void*, size_t, void*) {},
278- + nullptr);
279- + ab = ArrayBuffer::New(env->isolate(), std::move(backing));
280- + }
281- +
282- + if (ab->SetPrivate(env->context(),
283- + env->untransferable_object_private_symbol(),
284- + True(env->isolate()))
285- + .IsNothing()) {
286- + return;
287- + }
288- +
289- + args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1));
290- + }
291- +
292- static void Btoa(const FunctionCallbackInfo<Value>& args) {
293- CHECK_EQ(args.Length(), 1);
294- Environment* env = Environment::GetCurrent(args);
295- @@ -1420,7 +1460,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
127+ @@ -1420,7 +1420,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
296128 CHECK(value >= 0 && value < maxSize);
297129 size_t size = static_cast<size_t>(value);
298130 #ifdef V8_ENABLE_SANDBOX
@@ -301,22 +133,6 @@ index 49df0b4284748effb27b05ecd69f05699dd8be75..eb104ab80fd2052d1523cb2f2ecdb6d2
301133 #endif
302134 return size;
303135 }
304- @@ -1638,6 +1678,7 @@ void Initialize(Local<Object> target,
305- "utf8WriteStatic",
306- SlowWriteString<UTF8>,
307- &fast_write_string_utf8);
308- + SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
309- }
310-
311- } // anonymous namespace
312- @@ -1686,6 +1727,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
313- registry->Register(StringWrite<HEX>);
314- registry->Register(StringWrite<UCS2>);
315- registry->Register(StringWrite<UTF8>);
316- + registry->Register(GetZeroFillToggle);
317-
318- registry->Register(CopyArrayBuffer);
319- registry->Register(CreateUnsafeArrayBuffer);
320136diff --git a/src/node_i18n.cc b/src/node_i18n.cc
321137index 3c4f419aa29470b3280174b58680b9421b0340b5..3b24ad2a2316f89d98b067e2c13988f87a9a00d2 100644
322138--- a/src/node_i18n.cc
@@ -357,28 +173,6 @@ index 3c4f419aa29470b3280174b58680b9421b0340b5..3b24ad2a2316f89d98b067e2c13988f8
357173 }
358174
359175 constexpr const char* EncodingName(const enum encoding encoding) {
360- diff --git a/src/node_internals.h b/src/node_internals.h
361- index 61a58b6ccfb26efefd6d3b61a1c8741f9550ae8d..29d1ecc2b209c9c3c2e956263ba2d57fb688b34c 100644
362- --- a/src/node_internals.h
363- +++ b/src/node_internals.h
364- @@ -124,6 +124,9 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
365-
366- class NodeArrayBufferAllocator : public ArrayBufferAllocator {
367- public:
368- + NodeArrayBufferAllocator();
369- + ~NodeArrayBufferAllocator() override;
370- + inline uint32_t* zero_fill_field() { return zero_fill_field_; }
371- void* Allocate(size_t size) override; // Defined in src/node.cc
372- void* AllocateUninitialized(size_t size) override;
373- void Free(void* data, size_t size) override;
374- @@ -140,6 +143,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
375- }
376-
377- private:
378- + uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
379- std::atomic<size_t> total_mem_usage_ {0};
380-
381- // Delegate to V8's allocator for compatibility with the V8 memory cage.
382176diff --git a/src/node_serdes.cc b/src/node_serdes.cc
383177index 00fcd4b6afccce47ff21c4447d9cd60f25c11835..5f96ee2051e5339456185efddb149c4d43093f31 100644
384178--- a/src/node_serdes.cc
0 commit comments