Skip to content

Commit

Permalink
Reland "Implement updated array buffer allocator in gin"
Browse files Browse the repository at this point in the history
This is a reland of 714060d
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/570978
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Commit-Queue: Eric Holk <eholk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#487179}
  • Loading branch information
eholk authored and Commit Bot committed Jul 17, 2017
1 parent 42c1ee9 commit b2fa95d
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 1 deletion.
1 change: 1 addition & 0 deletions gin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ source_set("gin_test") {
test("gin_unittests") {
sources = [
"arguments_unittest.cc",
"array_buffer_unittest.cc",
"converter_unittest.cc",
"data_object_builder_unittest.cc",
"interceptor_unittest.cc",
Expand Down
88 changes: 87 additions & 1 deletion gin/array_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@
#include <stddef.h>
#include <stdlib.h>

#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "gin/array_buffer.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 {
Expand All @@ -30,10 +40,74 @@ void* ArrayBufferAllocator::AllocateUninitialized(size_t 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) {
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() {
static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
return instance;
Expand Down Expand Up @@ -82,6 +156,9 @@ class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> {
v8::Isolate* isolate_;
void* buffer_;
size_t length_;
void* allocation_base_;
size_t allocation_length_;
v8::ArrayBuffer::Allocator::AllocationMode allocation_mode_;
};

scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From(
Expand All @@ -104,6 +181,14 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate,
v8::ArrayBuffer::Contents contents = array->Externalize();
buffer_ = contents.Data();
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,
&g_array_buffer_wrapper_info);
Expand All @@ -115,7 +200,8 @@ ArrayBuffer::Private::Private(v8::Isolate* isolate,
}

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(
Expand Down
4 changes: 4 additions & 0 deletions gin/array_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
void* Allocate(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, AllocationMode mode) override;

void SetProtection(void* data, size_t length, Protection protection) override;

GIN_EXPORT static ArrayBufferAllocator* SharedInstance();
};
Expand Down
114 changes: 114 additions & 0 deletions gin/array_buffer_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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

0 comments on commit b2fa95d

Please sign in to comment.