diff options
Diffstat (limited to 'chromium/gin/array_buffer.cc')
-rw-r--r-- | chromium/gin/array_buffer.cc | 137 |
1 files changed, 133 insertions, 4 deletions
diff --git a/chromium/gin/array_buffer.cc b/chromium/gin/array_buffer.cc index 2e78aa60fc9..0adc026db8e 100644 --- a/chromium/gin/array_buffer.cc +++ b/chromium/gin/array_buffer.cc @@ -8,9 +8,13 @@ #include <stdlib.h> #include "base/allocator/partition_allocator/page_allocator.h" +#include "base/allocator/partition_allocator/partition_alloc.h" +#include "base/bits.h" #include "base/check_op.h" +#include "base/no_destructor.h" #include "build/build_config.h" #include "gin/per_isolate_data.h" +#include "v8/include/v8-initialization.h" #if BUILDFLAG(IS_POSIX) #include <sys/mman.h> @@ -26,25 +30,64 @@ static_assert(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2, "array buffers must have two internal fields"); // ArrayBufferAllocator ------------------------------------------------------- +base::ThreadSafePartitionRoot* ArrayBufferAllocator::partition_ = nullptr; void* ArrayBufferAllocator::Allocate(size_t length) { - // TODO(bbudge) Use partition allocator for malloc/calloc allocations. - return calloc(1, length); + unsigned int flags = partition_alloc::AllocFlags::kZeroFill | + partition_alloc::AllocFlags::kReturnNull; + return AllocateInternal(length, flags); } void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { - return malloc(length); + unsigned int flags = partition_alloc::AllocFlags::kReturnNull; + return AllocateInternal(length, flags); +} + +void* ArrayBufferAllocator::AllocateInternal(size_t length, + unsigned int flags) { +#ifdef V8_ENABLE_SANDBOX + // The V8 sandbox requires all ArrayBuffer backing stores to be allocated + // inside the sandbox address space. This isn't guaranteed if allocation + // override hooks (which are e.g. used by GWP-ASan) are enabled or if a + // memory tool (e.g. ASan) overrides malloc, so disable both. + flags |= partition_alloc::AllocFlags::kNoOverrideHooks; + flags |= partition_alloc::AllocFlags::kNoMemoryToolOverride; +#endif + return partition_->AllocWithFlags(flags, length, "gin::ArrayBufferAllocator"); } void ArrayBufferAllocator::Free(void* data, size_t length) { - free(data); + unsigned int flags = 0; +#ifdef V8_ENABLE_SANDBOX + // See |AllocateInternal|. + flags |= partition_alloc::FreeFlags::kNoMemoryToolOverride; +#endif + partition_->FreeWithFlags(flags, data); } +// static ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() { static ArrayBufferAllocator* instance = new ArrayBufferAllocator(); return instance; } +// static +void ArrayBufferAllocator::InitializePartition() { + static base::NoDestructor<base::PartitionAllocator> partition_allocator{}; + + // These configuration options are copied from blink's ArrayBufferPartition. + partition_allocator->init({ + base::PartitionOptions::AlignedAlloc::kDisallowed, + base::PartitionOptions::ThreadCache::kDisabled, + base::PartitionOptions::Quarantine::kAllowed, + base::PartitionOptions::Cookie::kAllowed, + base::PartitionOptions::BackupRefPtr::kDisabled, + base::PartitionOptions::UseConfigurablePool::kIfAvailable, + }); + + partition_ = partition_allocator->root(); +} + // ArrayBuffer ---------------------------------------------------------------- ArrayBuffer::ArrayBuffer() = default; @@ -96,4 +139,90 @@ bool Converter<ArrayBufferView>::FromV8(v8::Isolate* isolate, return true; } +// ArrayBufferSharedMemoryMapper --------------------------------------------- + +namespace { +#ifdef V8_ENABLE_SANDBOX +// When the V8 sandbox is enabled, shared memory backing ArrayBuffers must be +// mapped into the sandbox address space. This custom SharedMemoryMapper +// implements this. + +class ArrayBufferSharedMemoryMapper : public base::SharedMemoryMapper { + public: + absl::optional<base::span<uint8_t>> Map( + base::subtle::PlatformSharedMemoryHandle handle, + bool write_allowed, + uint64_t offset, + size_t size) override { + v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace(); + size_t allocation_granularity = address_space->allocation_granularity(); + + v8::PlatformSharedMemoryHandle v8_handle; +#if BUILDFLAG(IS_MAC) + v8_handle = v8::SharedMemoryHandleFromMachMemoryEntry(handle); +#elif BUILDFLAG(IS_FUCHSIA) + v8_handle = v8::SharedMemoryHandleFromVMO(handle->get()); +#elif BUILDFLAG(IS_WIN) + v8_handle = v8::SharedMemoryHandleFromFileMapping(handle); +#elif BUILDFLAG(IS_ANDROID) + v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle); +#elif BUILDFLAG(IS_POSIX) + v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle.fd); +#else +#error "Unknown platform" +#endif + + // Size and offset must be a multiple of the page allocation granularity. + // The caller already ensures that the offset is a multiple of the + // allocation granularity though. + CHECK_EQ(0UL, offset % allocation_granularity); + size_t mapping_size = base::bits::AlignUp(size, allocation_granularity); + + v8::PagePermissions permissions = write_allowed + ? v8::PagePermissions::kReadWrite + : v8::PagePermissions::kRead; + uintptr_t mapping = v8::V8::GetSandboxAddressSpace()->AllocateSharedPages( + 0, mapping_size, permissions, v8_handle, offset); + if (!mapping) + return absl::nullopt; + + return base::make_span(reinterpret_cast<uint8_t*>(mapping), size); + } + + void Unmap(base::span<uint8_t> mapping) override { + v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace(); + size_t allocation_granularity = address_space->allocation_granularity(); + + uintptr_t address = reinterpret_cast<uintptr_t>(mapping.data()); + CHECK_EQ(0UL, address % allocation_granularity); + size_t mapping_size = + base::bits::AlignUp(mapping.size(), allocation_granularity); + + address_space->FreeSharedPages(address, mapping_size); + } +}; +#endif // V8_ENABLE_SANDBOX + +base::SharedMemoryMapper* CreateSharedMemoryMapperForArrayBuffers() { +#if V8_ENABLE_SANDBOX + static ArrayBufferSharedMemoryMapper instance; + // Currently, it is still possible for the sandbox to be disabled at runtime + // (by not initializing it), in which case the default shared memory mapper + // must be used. In the future, this will no longer be allowed and this helper + // function can then be removed entirely. + // TODO(saelo) remove once sandbox initialization is mandatory. + if (v8::V8::GetSandboxSizeInBytes() > 0) + return &instance; + else +#endif + return base::SharedMemoryMapper::GetDefaultInstance(); +} +} // namespace + +base::SharedMemoryMapper* GetSharedMemoryMapperForArrayBuffers() { + static base::SharedMemoryMapper* mapper = + CreateSharedMemoryMapperForArrayBuffers(); + return mapper; +} + } // namespace gin |