diff --git a/src/api/environment.cc b/src/api/environment.cc index 8c14caa9c95f43..07363ea017a101 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -303,14 +303,17 @@ void SetIsolateUpForNode(v8::Isolate* isolate) { SetIsolateUpForNode(isolate, settings); } -// IsolateGroup GetOrCreateIsolateGroup() { - // When pointer compression is disabled, we cannot create new groups, - // in which case we'll always return the default. +#ifndef V8_ENABLE_SANDBOX + // When the V8 sandbox is enabled, all isolates must share the same sandbox + // so that ArrayBuffer backing stores allocated via NewDefaultAllocator() + // (which uses the default IsolateGroup's sandbox) are valid for all + // isolates. Creating new groups would give each group its own sandbox, + // causing a mismatch with the allocator. if (IsolateGroup::CanCreateNewGroups()) { return IsolateGroup::Create(); } - +#endif return IsolateGroup::GetDefault(); } diff --git a/src/node_buffer.cc b/src/node_buffer.cc index e40a21288ee79d..263eacb6c57cab 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -529,7 +529,7 @@ MaybeLocal New(Environment* env, } } -#if defined(V8_ENABLE_SANDBOX) +#ifdef V8_ENABLE_SANDBOX // When v8 sandbox is enabled, external backing stores are not supported // since all arraybuffer allocations are expected to be done by the isolate. // Since this violates the contract of this function, let's free the data and @@ -1453,7 +1453,7 @@ inline size_t CheckNumberToSize(Local number) { CHECK(value >= 0 && value < maxSize); size_t size = static_cast(value); #ifdef V8_ENABLE_SANDBOX - CHECK_LE(size, kMaxSafeBufferSizeForSandbox); + CHECK_LE(size, v8::internal::kMaxSafeBufferSizeForSandbox); #endif return size; } @@ -1476,6 +1476,24 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo& args) { env->isolate_data()->is_building_snapshot()) { buf = ArrayBuffer::New(isolate, size); } else { +#ifdef V8_ENABLE_SANDBOX + std::unique_ptr allocator( + ArrayBuffer::Allocator::NewDefaultAllocator()); + void* data = allocator->AllocateUninitialized(size); + if (!data) [[unlikely]] { + THROW_ERR_MEMORY_ALLOCATION_FAILED(env); + return; + } + std::unique_ptr store = ArrayBuffer::NewBackingStore( + data, + size, + [](void* data, size_t length, void*) { + std::unique_ptr allocator( + ArrayBuffer::Allocator::NewDefaultAllocator()); + allocator->Free(data, length); + }, + nullptr); +#else std::unique_ptr store = ArrayBuffer::NewBackingStore( isolate, size, @@ -1486,6 +1504,7 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo& args) { THROW_ERR_MEMORY_ALLOCATION_FAILED(env); return; } +#endif buf = ArrayBuffer::New(isolate, std::move(store)); } diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 00fcd4b6afccce..59f87cba19f224 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -29,6 +29,26 @@ using v8::ValueSerializer; namespace serdes { +v8::ArrayBuffer::Allocator* GetAllocator() { + static v8::ArrayBuffer::Allocator* allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + return allocator; +} + +void* Reallocate(void* data, size_t old_length, size_t new_length) { + if (old_length == new_length) return data; + uint8_t* new_data = reinterpret_cast( + GetAllocator()->AllocateUninitialized(new_length)); + if (new_data == nullptr) return nullptr; + size_t bytes_to_copy = std::min(old_length, new_length); + memcpy(new_data, data, bytes_to_copy); + if (new_length > bytes_to_copy) { + memset(new_data + bytes_to_copy, 0, new_length - bytes_to_copy); + } + GetAllocator()->Free(data, old_length); + return new_data; +} + class SerializerContext : public BaseObject, public ValueSerializer::Delegate { public: @@ -37,10 +57,15 @@ class SerializerContext : public BaseObject, ~SerializerContext() override = default; + // v8::ValueSerializer::Delegate void ThrowDataCloneError(Local message) override; Maybe WriteHostObject(Isolate* isolate, Local object) override; Maybe GetSharedArrayBufferId( Isolate* isolate, Local shared_array_buffer) override; + void* ReallocateBufferMemory(void* old_buffer, + size_t old_length, + size_t* new_length) override; + void FreeBufferMemory(void* buffer) override; static void SetTreatArrayBufferViewsAsHostObjects( const FunctionCallbackInfo& args); @@ -61,6 +86,7 @@ class SerializerContext : public BaseObject, private: ValueSerializer serializer_; + size_t last_length_ = 0; }; class DeserializerContext : public BaseObject, @@ -145,6 +171,24 @@ Maybe SerializerContext::GetSharedArrayBufferId( return id->Uint32Value(env()->context()); } +void* SerializerContext::ReallocateBufferMemory(void* old_buffer, + size_t requested_size, + size_t* new_length) { + *new_length = std::max(static_cast(4096), requested_size); + if (old_buffer) { + void* ret = Reallocate(old_buffer, last_length_, *new_length); + last_length_ = *new_length; + return ret; + } else { + last_length_ = *new_length; + return GetAllocator()->Allocate(*new_length); + } +} + +void SerializerContext::FreeBufferMemory(void* buffer) { + GetAllocator()->Free(buffer, last_length_); +} + Maybe SerializerContext::WriteHostObject(Isolate* isolate, Local input) { Local args[1] = { input }; @@ -208,14 +252,27 @@ void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo& args) { SerializerContext* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.This()); - // Note: Both ValueSerializer and this Buffer::New() variant use malloc() - // as the underlying allocator. std::pair ret = ctx->serializer_.Release(); +#ifdef V8_ENABLE_SANDBOX Local buf; if (Buffer::New(ctx->env(), reinterpret_cast(ret.first), ret.second) .ToLocal(&buf)) { args.GetReturnValue().Set(buf); } +#else + std::unique_ptr bs = v8::ArrayBuffer::NewBackingStore( + reinterpret_cast(ret.first), + ret.second, + [](void* data, size_t length, void* deleter_data) { + if (data) GetAllocator()->Free(reinterpret_cast(data), length); + }, + nullptr); + Local ab = + v8::ArrayBuffer::New(ctx->env()->isolate(), std::move(bs)); + + auto buf = Buffer::New(ctx->env(), ab, 0, ret.second); + if (!buf.IsEmpty()) args.GetReturnValue().Set(buf.ToLocalChecked()); +#endif } void SerializerContext::TransferArrayBuffer( diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index ef659f1c39f7ee..6f401807ab7c7c 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -129,12 +129,28 @@ static void GetCategoryEnabledBuffer(const FunctionCallbackInfo& args) { const uint8_t* enabled_pointer = TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_name.out()); uint8_t* enabled_pointer_cast = const_cast(enabled_pointer); - + uint8_t size = sizeof(*enabled_pointer_cast); + +#ifdef V8_ENABLE_SANDBOX + std::unique_ptr allocator( + ArrayBuffer::Allocator::NewDefaultAllocator()); + void* v8_data = allocator->Allocate(size); + CHECK(v8_data); + memcpy(v8_data, enabled_pointer_cast, size); std::unique_ptr bs = ArrayBuffer::NewBackingStore( - enabled_pointer_cast, - sizeof(*enabled_pointer_cast), - [](void*, size_t, void*) {}, + v8_data, + size, + [](void* data, size_t length, void*) { + std::unique_ptr allocator( + ArrayBuffer::Allocator::NewDefaultAllocator()); + allocator->Free(data, length); + }, nullptr); +#else + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + enabled_pointer_cast, size, [](void*, size_t, void*) {}, nullptr); +#endif + auto ab = ArrayBuffer::New(isolate, std::move(bs)); v8::Local u8 = v8::Uint8Array::New(ab, 0, 1);