Commit b2fa95d4 authored by Eric Holk's avatar Eric Holk Committed by Commit Bot

Reland "Implement updated array buffer allocator in gin"

This is a reland of 714060db
Original change's description:
> Implement updated array buffer allocator in gin
> 
> Bug: chromium:720302
> Change-Id: I127daca6ee9954774b8ff23382ba38cb23da7318
> Reviewed-on: https://chromium-review.googlesource.com/543670
> Commit-Queue: Eric Holk <eholk@chromium.org>
> Reviewed-by: Jochen Eisinger <jochen@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#481959}

Bug: chromium:720302
Change-Id: I880773c83d51c8ec0d3d55dfa8913e6b3a16ddec
Reviewed-on: https://chromium-review.googlesource.com/570978Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Commit-Queue: Eric Holk <eholk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#487179}
parent 42c1ee98
...@@ -143,6 +143,7 @@ source_set("gin_test") { ...@@ -143,6 +143,7 @@ source_set("gin_test") {
test("gin_unittests") { test("gin_unittests") {
sources = [ sources = [
"arguments_unittest.cc", "arguments_unittest.cc",
"array_buffer_unittest.cc",
"converter_unittest.cc", "converter_unittest.cc",
"data_object_builder_unittest.cc", "data_object_builder_unittest.cc",
"interceptor_unittest.cc", "interceptor_unittest.cc",
......
...@@ -5,10 +5,20 @@ ...@@ -5,10 +5,20 @@
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/logging.h" #include "base/logging.h"
#include "build/build_config.h"
#include "gin/array_buffer.h" #include "gin/array_buffer.h"
#include "gin/per_isolate_data.h" #include "gin/per_isolate_data.h"
#if defined(OS_POSIX)
#include <sys/mman.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
namespace gin { namespace gin {
namespace { namespace {
...@@ -30,10 +40,74 @@ void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { ...@@ -30,10 +40,74 @@ void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
return malloc(length); return malloc(length);
} }
void* ArrayBufferAllocator::Reserve(size_t length) {
void* const hint = nullptr;
#if defined(OS_POSIX)
int const access_flag = PROT_NONE;
void* const ret =
mmap(hint, length, access_flag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (ret == MAP_FAILED) {
return nullptr;
}
return ret;
#else
DWORD const access_flag = PAGE_NOACCESS;
return VirtualAlloc(hint, length, MEM_RESERVE, access_flag);
#endif
}
void ArrayBufferAllocator::Free(void* data, size_t length) { void ArrayBufferAllocator::Free(void* data, size_t length) {
free(data); free(data);
} }
void ArrayBufferAllocator::Free(void* data,
size_t length,
AllocationMode mode) {
switch (mode) {
case AllocationMode::kNormal:
Free(data, length);
return;
case AllocationMode::kReservation: {
#if defined(OS_POSIX)
int const ret = munmap(data, length);
CHECK(!ret);
#else
BOOL const ret = VirtualFree(data, 0, MEM_RELEASE);
CHECK(ret);
#endif
return;
}
default:
NOTREACHED();
}
}
void ArrayBufferAllocator::SetProtection(void* data,
size_t length,
Protection protection) {
switch (protection) {
case Protection::kNoAccess: {
#if defined(OS_POSIX)
int ret = mprotect(data, length, PROT_NONE);
CHECK(!ret);
#else
BOOL ret = VirtualFree(data, length, MEM_DECOMMIT);
CHECK(ret);
#endif
break;
}
case Protection::kReadWrite:
#if defined(OS_POSIX)
mprotect(data, length, PROT_READ | PROT_WRITE);
#else
VirtualAlloc(data, length, MEM_COMMIT, PAGE_READWRITE);
#endif
break;
default:
NOTREACHED();
}
}
ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() { ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
static ArrayBufferAllocator* instance = new ArrayBufferAllocator(); static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
return instance; return instance;
...@@ -82,6 +156,9 @@ class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> { ...@@ -82,6 +156,9 @@ class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> {
v8::Isolate* isolate_; v8::Isolate* isolate_;
void* buffer_; void* buffer_;
size_t length_; size_t length_;
void* allocation_base_;
size_t allocation_length_;
v8::ArrayBuffer::Allocator::AllocationMode allocation_mode_;
}; };
scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From( scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From(
...@@ -104,6 +181,14 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate, ...@@ -104,6 +181,14 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate,
v8::ArrayBuffer::Contents contents = array->Externalize(); v8::ArrayBuffer::Contents contents = array->Externalize();
buffer_ = contents.Data(); buffer_ = contents.Data();
length_ = contents.ByteLength(); length_ = contents.ByteLength();
allocation_base_ = contents.AllocationBase();
allocation_length_ = contents.AllocationLength();
allocation_mode_ = contents.AllocationMode();
DCHECK(reinterpret_cast<uintptr_t>(allocation_base_) <=
reinterpret_cast<uintptr_t>(buffer_));
DCHECK(reinterpret_cast<uintptr_t>(buffer_) + length_ <=
reinterpret_cast<uintptr_t>(allocation_base_) + allocation_length_);
array->SetAlignedPointerInInternalField(kWrapperInfoIndex, array->SetAlignedPointerInInternalField(kWrapperInfoIndex,
&g_array_buffer_wrapper_info); &g_array_buffer_wrapper_info);
...@@ -115,7 +200,8 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate, ...@@ -115,7 +200,8 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate,
} }
ArrayBuffer::Private::~Private() { ArrayBuffer::Private::~Private() {
PerIsolateData::From(isolate_)->allocator()->Free(buffer_, length_); PerIsolateData::From(isolate_)->allocator()->Free(
allocation_base_, allocation_length_, allocation_mode_);
} }
void ArrayBuffer::Private::FirstWeakCallback( void ArrayBuffer::Private::FirstWeakCallback(
......
...@@ -21,7 +21,11 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { ...@@ -21,7 +21,11 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public: public:
void* Allocate(size_t length) override; void* Allocate(size_t length) override;
void* AllocateUninitialized(size_t length) override; void* AllocateUninitialized(size_t length) override;
void* Reserve(size_t length) override;
void Free(void* data, size_t length) override; void Free(void* data, size_t length) override;
void Free(void* data, size_t length, AllocationMode mode) override;
void SetProtection(void* data, size_t length, Protection protection) override;
GIN_EXPORT static ArrayBufferAllocator* SharedInstance(); GIN_EXPORT static ArrayBufferAllocator* SharedInstance();
}; };
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gin/array_buffer.h"
#include "build/build_config.h"
#include "gin/per_isolate_data.h"
#include "gin/public/isolate_holder.h"
#include "gin/test/v8_test.h"
#if defined(OS_POSIX)
#include <setjmp.h>
#include <signal.h>
#endif
namespace gin {
using ArrayBufferTest = V8Test;
namespace {
const size_t kBufferLength = 65536;
}
TEST_F(ArrayBufferTest, AllocateAndFreeBuffer) {
v8::Isolate* const isolate = instance_->isolate();
v8::ArrayBuffer::Allocator* const allocator =
PerIsolateData::From(isolate)->allocator();
void* buffer = allocator->Allocate(kBufferLength);
allocator->Free(buffer, kBufferLength);
}
TEST_F(ArrayBufferTest, ReserveAndReleaseBuffer) {
v8::Isolate* const isolate = instance_->isolate();
v8::ArrayBuffer::Allocator* const allocator =
PerIsolateData::From(isolate)->allocator();
void* buffer = allocator->Reserve(kBufferLength);
allocator->Free(buffer, kBufferLength,
v8::ArrayBuffer::Allocator::AllocationMode::kReservation);
}
TEST_F(ArrayBufferTest, SetProtectionReadWrite) {
v8::Isolate* const isolate = instance_->isolate();
v8::ArrayBuffer::Allocator* const allocator =
PerIsolateData::From(isolate)->allocator();
void* buffer = allocator->Reserve(kBufferLength);
allocator->SetProtection(buffer, kBufferLength,
v8::ArrayBuffer::Allocator::Protection::kReadWrite);
volatile int* int_buffer = static_cast<volatile int*>(buffer);
// Try assigning to the buffer. This will fault if we don't SetProtection
// first.
int_buffer[0] = 42;
allocator->Free(buffer, kBufferLength,
v8::ArrayBuffer::Allocator::AllocationMode::kReservation);
}
#if defined(OS_POSIX)
namespace {
sigjmp_buf g_continuation_;
void SignalHandler(int signal, siginfo_t* info, void*) {
siglongjmp(g_continuation_, 1);
}
} // namespace
TEST_F(ArrayBufferTest, ReservationReadOnlyByDefault) {
v8::Isolate* const isolate = instance_->isolate();
v8::ArrayBuffer::Allocator* const allocator =
PerIsolateData::From(isolate)->allocator();
void* buffer = allocator->Reserve(kBufferLength);
volatile int* int_buffer = static_cast<volatile int*>(buffer);
// Install a signal handler so we can catch the fault we're about to trigger.
struct sigaction action = {};
struct sigaction old_action = {};
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, &old_action);
#if defined(OS_MACOSX)
// On Mac, sometimes we get SIGBUS instead of SIGSEGV.
struct sigaction old_bus_action;
sigaction(SIGBUS, &action, &old_bus_action);
#endif
int const save_sigs = 1;
if (!sigsetjmp(g_continuation_, save_sigs)) {
// Try assigning to the buffer. This will fault if we don't SetProtection
// first.
int_buffer[0] = 42;
} else {
// if sigsetjmp returns nonzero, then we are returning from our handler.
sigaction(SIGSEGV, &old_action, nullptr);
#if defined(OS_MACOSX)
sigaction(SIGBUS, &old_bus_action, nullptr);
#endif
allocator->Free(buffer, kBufferLength,
v8::ArrayBuffer::Allocator::AllocationMode::kReservation);
SUCCEED();
return;
}
FAIL();
}
#endif // OS_POSIX
} // namespace gin
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment