Skip to content

Commit

Permalink
Implement updated array buffer allocator in gin
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
eholk authored and Commit Bot committed Jun 23, 2017
1 parent ac7b23b commit 714060d
Show file tree
Hide file tree
Showing 4 changed files with 171 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
54 changes: 53 additions & 1 deletion gin/array_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <stddef.h>
#include <stdlib.h>

#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/logging.h"
#include "gin/array_buffer.h"
#include "gin/per_isolate_data.h"
Expand All @@ -30,10 +31,49 @@ void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
return malloc(length);
}

void* ArrayBufferAllocator::Reserve(size_t length) {
void* const hint = nullptr;
const size_t align = 64 << 10; // Wasm page size
// TODO(eholk): On Windows this commits all the memory, rather than
// just reserving it. This is very bad and should be fixed, but we don't use
// this feature on Windows at all yet.
return base::AllocPages(hint, length, align, base::PageInaccessible);
}

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:
base::FreePages(data, length);
return;
default:
NOTREACHED();
}
}

void ArrayBufferAllocator::SetProtection(void* data,
size_t length,
Protection protection) {
switch (protection) {
case Protection::kNoAccess:
base::SetSystemPagesInaccessible(data, length);
break;
case Protection::kReadWrite:
ignore_result(base::SetSystemPagesAccessible(data, length));
break;
default:
NOTREACHED();
}
}

ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() {
static ArrayBufferAllocator* instance = new ArrayBufferAllocator();
return instance;
Expand Down Expand Up @@ -82,6 +122,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 +147,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 +166,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
113 changes: 113 additions & 0 deletions gin/array_buffer_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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 "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 714060d

Please sign in to comment.