1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gin/array_buffer.h"
10 #include "base/allocator/partition_allocator/src/partition_alloc/page_allocator.h"
11 #include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc.h"
12 #include "base/allocator/partition_allocator/src/partition_alloc/partition_root.h"
13 #include "base/bits.h"
14 #include "base/check_op.h"
15 #include "base/no_destructor.h"
16 #include "build/build_config.h"
17 #include "gin/per_isolate_data.h"
18 #include "v8/include/v8-initialization.h"
20 #if BUILDFLAG(IS_POSIX)
24 #define MAP_ANONYMOUS MAP_ANON
26 #endif // BUILDFLAG(IS_POSIX)
30 static_assert(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2,
31 "array buffers must have two internal fields");
33 // ArrayBufferAllocator -------------------------------------------------------
34 partition_alloc::PartitionRoot* ArrayBufferAllocator::partition_ = nullptr;
36 void* ArrayBufferAllocator::Allocate(size_t length) {
37 constexpr partition_alloc::AllocFlags flags =
38 partition_alloc::AllocFlags::kZeroFill |
39 partition_alloc::AllocFlags::kReturnNull;
40 return AllocateInternal<flags>(length);
43 void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
44 constexpr partition_alloc::AllocFlags flags =
45 partition_alloc::AllocFlags::kReturnNull;
46 return AllocateInternal<flags>(length);
49 template <partition_alloc::AllocFlags flags>
50 void* ArrayBufferAllocator::AllocateInternal(size_t length) {
51 #ifdef V8_ENABLE_SANDBOX
52 // The V8 sandbox requires all ArrayBuffer backing stores to be allocated
53 // inside the sandbox address space. This isn't guaranteed if allocation
54 // override hooks (which are e.g. used by GWP-ASan) are enabled or if a
55 // memory tool (e.g. ASan) overrides malloc, so disable both.
56 constexpr auto new_flags = flags |
57 partition_alloc::AllocFlags::kNoOverrideHooks |
58 partition_alloc::AllocFlags::kNoMemoryToolOverride;
60 constexpr auto new_flags = flags;
62 return partition_->AllocInline<new_flags>(length,
63 "gin::ArrayBufferAllocator");
66 void ArrayBufferAllocator::Free(void* data, size_t length) {
67 #ifdef V8_ENABLE_SANDBOX
68 // See |AllocateMemoryWithFlags|.
69 partition_->Free<partition_alloc::FreeFlags::kNoMemoryToolOverride>(data);
71 partition_->Free(data);
76 ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
77 static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
82 void ArrayBufferAllocator::InitializePartition() {
83 static base::NoDestructor<partition_alloc::PartitionAllocator>
84 partition_allocator(partition_alloc::PartitionOptions{
85 .star_scan_quarantine = partition_alloc::PartitionOptions::kAllowed,
86 .backup_ref_ptr = partition_alloc::PartitionOptions::kDisabled,
87 .use_configurable_pool = partition_alloc::PartitionOptions::kAllowed,
90 partition_ = partition_allocator->root();
93 // ArrayBuffer ----------------------------------------------------------------
94 ArrayBuffer::ArrayBuffer() = default;
96 ArrayBuffer::ArrayBuffer(v8::Isolate* isolate, v8::Local<v8::ArrayBuffer> array)
97 : backing_store_(array->GetBackingStore()) {}
99 ArrayBuffer::~ArrayBuffer() = default;
101 ArrayBuffer& ArrayBuffer::operator=(const ArrayBuffer& other) = default;
103 // Converter<ArrayBuffer> -----------------------------------------------------
105 bool Converter<ArrayBuffer>::FromV8(v8::Isolate* isolate,
106 v8::Local<v8::Value> val,
108 if (!val->IsArrayBuffer())
110 *out = ArrayBuffer(isolate, v8::Local<v8::ArrayBuffer>::Cast(val));
114 // ArrayBufferView ------------------------------------------------------------
116 ArrayBufferView::ArrayBufferView()
121 ArrayBufferView::ArrayBufferView(v8::Isolate* isolate,
122 v8::Local<v8::ArrayBufferView> view)
123 : array_buffer_(isolate, view->Buffer()),
124 offset_(view->ByteOffset()),
125 num_bytes_(view->ByteLength()) {
128 ArrayBufferView::~ArrayBufferView() = default;
130 ArrayBufferView& ArrayBufferView::operator=(const ArrayBufferView& other) =
133 // Converter<ArrayBufferView> -------------------------------------------------
135 bool Converter<ArrayBufferView>::FromV8(v8::Isolate* isolate,
136 v8::Local<v8::Value> val,
137 ArrayBufferView* out) {
138 if (!val->IsArrayBufferView())
140 *out = ArrayBufferView(isolate, v8::Local<v8::ArrayBufferView>::Cast(val));
144 // ArrayBufferSharedMemoryMapper ---------------------------------------------
147 #ifdef V8_ENABLE_SANDBOX
148 // When the V8 sandbox is enabled, shared memory backing ArrayBuffers must be
149 // mapped into the sandbox address space. This custom SharedMemoryMapper
152 class ArrayBufferSharedMemoryMapper : public base::SharedMemoryMapper {
154 absl::optional<base::span<uint8_t>> Map(
155 base::subtle::PlatformSharedMemoryHandle handle,
158 size_t size) override {
159 v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace();
160 size_t allocation_granularity = address_space->allocation_granularity();
162 v8::PlatformSharedMemoryHandle v8_handle;
163 #if BUILDFLAG(IS_APPLE)
164 v8_handle = v8::SharedMemoryHandleFromMachMemoryEntry(handle);
165 #elif BUILDFLAG(IS_FUCHSIA)
166 v8_handle = v8::SharedMemoryHandleFromVMO(handle->get());
167 #elif BUILDFLAG(IS_WIN)
168 v8_handle = v8::SharedMemoryHandleFromFileMapping(handle);
169 #elif BUILDFLAG(IS_ANDROID)
170 v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle);
171 #elif BUILDFLAG(IS_POSIX)
172 v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle.fd);
174 #error "Unknown platform"
177 // Size and offset must be a multiple of the page allocation granularity.
178 // The caller already ensures that the offset is a multiple of the
179 // allocation granularity though.
180 CHECK_EQ(0UL, offset % allocation_granularity);
181 size_t mapping_size = base::bits::AlignUp(size, allocation_granularity);
183 v8::PagePermissions permissions = write_allowed
184 ? v8::PagePermissions::kReadWrite
185 : v8::PagePermissions::kRead;
186 uintptr_t mapping = v8::V8::GetSandboxAddressSpace()->AllocateSharedPages(
187 0, mapping_size, permissions, v8_handle, offset);
189 return absl::nullopt;
191 return base::make_span(reinterpret_cast<uint8_t*>(mapping), size);
194 void Unmap(base::span<uint8_t> mapping) override {
195 v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace();
196 size_t allocation_granularity = address_space->allocation_granularity();
198 uintptr_t address = reinterpret_cast<uintptr_t>(mapping.data());
199 CHECK_EQ(0UL, address % allocation_granularity);
200 size_t mapping_size =
201 base::bits::AlignUp(mapping.size(), allocation_granularity);
203 address_space->FreeSharedPages(address, mapping_size);
206 #endif // V8_ENABLE_SANDBOX
209 base::SharedMemoryMapper* GetSharedMemoryMapperForArrayBuffers() {
210 #if V8_ENABLE_SANDBOX
211 static ArrayBufferSharedMemoryMapper instance;
214 return base::SharedMemoryMapper::GetDefaultInstance();