Skip to content

Commit 18a26cf

Browse files
eholkCommit Bot
authored andcommitted
Add memory protection API to ArrayBuffer::Allocator
WebAssembly needs to be able to allocate memory with guard regions, which requires more functionality from the array buffer allocator. This change adds functions for reserving memory regions and changing the memory protection. This CL also includes some minor refactoring of the code to free array buffers. Bug: chromium:720302 Change-Id: Iab9a266003043b0d36592a79668d1eea53952abf Reviewed-on: https://chromium-review.googlesource.com/506377 Reviewed-by: Jochen Eisinger <[email protected]> Reviewed-by: Michael Lippautz <[email protected]> Commit-Queue: Eric Holk <[email protected]> Cr-Commit-Position: refs/heads/master@{#45407}
1 parent 5dc2d6f commit 18a26cf

File tree

8 files changed

+203
-21
lines changed

8 files changed

+203
-21
lines changed

include/v8.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4186,12 +4186,42 @@ class V8_EXPORT ArrayBuffer : public Object {
41864186
*/
41874187
virtual void* AllocateUninitialized(size_t length) = 0;
41884188

4189+
/**
4190+
* Reserved |length| bytes, but do not commit the memory. Must call
4191+
* |SetProtection| to make memory accessible.
4192+
*/
4193+
// TODO(eholk): make this pure virtual once blink implements this.
4194+
virtual void* Reserve(size_t length);
4195+
41894196
/**
41904197
* Free the memory block of size |length|, pointed to by |data|.
41914198
* That memory is guaranteed to be previously allocated by |Allocate|.
41924199
*/
41934200
virtual void Free(void* data, size_t length) = 0;
41944201

4202+
enum class AllocationMode { kNormal, kReservation };
4203+
4204+
/**
4205+
* Free the memory block of size |length|, pointed to by |data|.
4206+
* That memory is guaranteed to be previously allocated by |Allocate| or
4207+
* |Reserve|, depending on |mode|.
4208+
*/
4209+
// TODO(eholk): make this pure virtual once blink implements this.
4210+
virtual void Free(void* data, size_t length, AllocationMode mode);
4211+
4212+
enum class Protection { kNoAccess, kReadWrite };
4213+
4214+
/**
4215+
* Change the protection on a region of memory.
4216+
*
4217+
* On platforms that make a distinction between reserving and committing
4218+
* memory, changing the protection to kReadWrite must also ensure the memory
4219+
* is committed.
4220+
*/
4221+
// TODO(eholk): make this pure virtual once blink implements this.
4222+
virtual void SetProtection(void* data, size_t length,
4223+
Protection protection);
4224+
41954225
/**
41964226
* malloc/free based convenience allocator.
41974227
*

src/api.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,19 @@ void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) {
441441
i::V8::SetSnapshotBlob(snapshot_blob);
442442
}
443443

444+
void* v8::ArrayBuffer::Allocator::Reserve(size_t length) { UNIMPLEMENTED(); }
445+
446+
void v8::ArrayBuffer::Allocator::Free(void* data, size_t length,
447+
AllocationMode mode) {
448+
UNIMPLEMENTED();
449+
}
450+
451+
void v8::ArrayBuffer::Allocator::SetProtection(
452+
void* data, size_t length,
453+
v8::ArrayBuffer::Allocator::Protection protection) {
454+
UNIMPLEMENTED();
455+
}
456+
444457
namespace {
445458

446459
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
@@ -451,6 +464,39 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
451464
}
452465
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
453466
virtual void Free(void* data, size_t) { free(data); }
467+
468+
virtual void* Reserve(size_t length) {
469+
return base::VirtualMemory::ReserveRegion(length);
470+
}
471+
472+
virtual void Free(void* data, size_t length,
473+
v8::ArrayBuffer::Allocator::AllocationMode mode) {
474+
switch (mode) {
475+
case v8::ArrayBuffer::Allocator::AllocationMode::kNormal: {
476+
return Free(data, length);
477+
}
478+
case v8::ArrayBuffer::Allocator::AllocationMode::kReservation: {
479+
base::VirtualMemory::ReleaseRegion(data, length);
480+
return;
481+
}
482+
}
483+
}
484+
485+
virtual void SetProtection(
486+
void* data, size_t length,
487+
v8::ArrayBuffer::Allocator::Protection protection) {
488+
switch (protection) {
489+
case v8::ArrayBuffer::Allocator::Protection::kNoAccess: {
490+
base::VirtualMemory::UncommitRegion(data, length);
491+
return;
492+
}
493+
case v8::ArrayBuffer::Allocator::Protection::kReadWrite: {
494+
const bool is_executable = false;
495+
base::VirtualMemory::CommitRegion(data, length, is_executable);
496+
return;
497+
}
498+
}
499+
}
454500
};
455501

456502
bool RunExtraCode(Isolate* isolate, Local<Context> context,

src/d8.cc

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,37 @@ const int kMaxSerializerMemoryUsage = 1 * MB; // Arbitrary maximum for testing.
7373
// array buffers storing the lengths as a SMI internally.
7474
#define TWO_GB (2u * 1024u * 1024u * 1024u)
7575

76-
class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
76+
// Forwards memory reservation and protection functions to the V8 default
77+
// allocator. Used by ShellArrayBufferAllocator and MockArrayBufferAllocator.
78+
class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
79+
std::unique_ptr<Allocator> allocator_ =
80+
std::unique_ptr<Allocator>(NewDefaultAllocator());
81+
7782
public:
78-
virtual void* Allocate(size_t length) {
83+
void* Reserve(size_t length) override { return allocator_->Reserve(length); }
84+
85+
void Free(void*, size_t) override = 0;
86+
87+
void Free(void* data, size_t length, AllocationMode mode) override {
88+
switch (mode) {
89+
case AllocationMode::kNormal: {
90+
return Free(data, length);
91+
}
92+
case AllocationMode::kReservation: {
93+
return allocator_->Free(data, length, mode);
94+
}
95+
}
96+
}
97+
98+
void SetProtection(void* data, size_t length,
99+
Protection protection) override {
100+
allocator_->SetProtection(data, length, protection);
101+
}
102+
};
103+
104+
class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
105+
public:
106+
void* Allocate(size_t length) override {
79107
#if USE_VM
80108
if (RoundToPageSize(&length)) {
81109
void* data = VirtualMemoryAllocate(length);
@@ -95,7 +123,7 @@ class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
95123
void* data = AllocateUninitialized(length);
96124
return data == NULL ? data : memset(data, 0, length);
97125
}
98-
virtual void* AllocateUninitialized(size_t length) {
126+
void* AllocateUninitialized(size_t length) override {
99127
#if USE_VM
100128
if (RoundToPageSize(&length)) return VirtualMemoryAllocate(length);
101129
#endif
@@ -107,7 +135,7 @@ class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
107135
return malloc(length);
108136
#endif
109137
}
110-
virtual void Free(void* data, size_t length) {
138+
void Free(void* data, size_t length) override {
111139
#if USE_VM
112140
if (RoundToPageSize(&length)) {
113141
base::VirtualMemory::ReleaseRegion(data, length);
@@ -139,18 +167,28 @@ class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
139167
#endif
140168
};
141169

170+
class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
171+
const size_t kAllocationLimit = 10 * MB;
172+
size_t get_actual_length(size_t length) const {
173+
return length > kAllocationLimit ? base::OS::CommitPageSize() : length;
174+
}
142175

143-
class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
144176
public:
145177
void* Allocate(size_t length) override {
146-
size_t actual_length = length > 10 * MB ? 1 : length;
178+
const size_t actual_length = get_actual_length(length);
147179
void* data = AllocateUninitialized(actual_length);
148180
return data == NULL ? data : memset(data, 0, actual_length);
149181
}
150182
void* AllocateUninitialized(size_t length) override {
151-
return length > 10 * MB ? malloc(1) : malloc(length);
183+
return malloc(get_actual_length(length));
152184
}
153185
void Free(void* p, size_t) override { free(p); }
186+
void Free(void* data, size_t length, AllocationMode mode) override {
187+
ArrayBufferAllocatorBase::Free(data, get_actual_length(length), mode);
188+
}
189+
void* Reserve(size_t length) override {
190+
return ArrayBufferAllocatorBase::Reserve(get_actual_length(length));
191+
}
154192
};
155193

156194

src/heap/array-buffer-tracker.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ void LocalArrayBufferTracker::Free(Callback should_free) {
2222
JSArrayBuffer* buffer = reinterpret_cast<JSArrayBuffer*>(it->first);
2323
if (should_free(buffer)) {
2424
const size_t len = it->second;
25-
heap_->isolate()->array_buffer_allocator()->Free(buffer->backing_store(),
26-
len);
25+
buffer->FreeBackingStore();
26+
2727
freed_memory += len;
2828
it = array_buffers_.erase(it);
2929
} else {
@@ -62,8 +62,7 @@ void LocalArrayBufferTracker::Process(Callback callback) {
6262
it = array_buffers_.erase(it);
6363
} else if (result == kRemoveEntry) {
6464
const size_t len = it->second;
65-
heap_->isolate()->array_buffer_allocator()->Free(
66-
it->first->backing_store(), len);
65+
it->first->FreeBackingStore();
6766
freed_memory += len;
6867
it = array_buffers_.erase(it);
6968
} else {

src/objects-body-descriptors-inl.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,25 +210,31 @@ class JSFunction::BodyDescriptorImpl final : public BodyDescriptorBase {
210210
class JSArrayBuffer::BodyDescriptor final : public BodyDescriptorBase {
211211
public:
212212
STATIC_ASSERT(kByteLengthOffset + kPointerSize == kBackingStoreOffset);
213-
STATIC_ASSERT(kBackingStoreOffset + kPointerSize == kBitFieldSlot);
213+
STATIC_ASSERT(kAllocationLengthOffset + kPointerSize == kBitFieldSlot);
214214
STATIC_ASSERT(kBitFieldSlot + kPointerSize == kSize);
215215

216216
static bool IsValidSlot(HeapObject* obj, int offset) {
217-
if (offset < kBackingStoreOffset) return true;
217+
if (offset < kAllocationLengthOffset) return true;
218218
if (offset < kSize) return false;
219219
return IsValidSlotImpl(obj, offset);
220220
}
221221

222222
template <typename ObjectVisitor>
223223
static inline void IterateBody(HeapObject* obj, int object_size,
224224
ObjectVisitor* v) {
225+
// Array buffers contain raw pointers that the GC does not know about. These
226+
// are stored at kBackStoreOffset and later, so we do not iterate over
227+
// those.
225228
IteratePointers(obj, kPropertiesOffset, kBackingStoreOffset, v);
226229
IterateBodyImpl(obj, kSize, object_size, v);
227230
}
228231

229232
template <typename StaticVisitor>
230233
static inline void IterateBody(HeapObject* obj, int object_size) {
231234
Heap* heap = obj->GetHeap();
235+
// Array buffers contain raw pointers that the GC does not know about. These
236+
// are stored at kBackStoreOffset and later, so we do not iterate over
237+
// those.
232238
IteratePointers<StaticVisitor>(heap, obj, kPropertiesOffset,
233239
kBackingStoreOffset);
234240
IterateBodyImpl<StaticVisitor>(heap, obj, kSize, object_size);

src/objects-inl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6904,6 +6904,25 @@ void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) {
69046904

69056905
ACCESSORS(JSArrayBuffer, byte_length, Object, kByteLengthOffset)
69066906

6907+
void* JSArrayBuffer::allocation_base() const {
6908+
intptr_t ptr = READ_INTPTR_FIELD(this, kAllocationBaseOffset);
6909+
return reinterpret_cast<void*>(ptr);
6910+
}
6911+
6912+
void JSArrayBuffer::set_allocation_base(void* value, WriteBarrierMode mode) {
6913+
intptr_t ptr = reinterpret_cast<intptr_t>(value);
6914+
WRITE_INTPTR_FIELD(this, kAllocationBaseOffset, ptr);
6915+
}
6916+
6917+
size_t JSArrayBuffer::allocation_length() const {
6918+
return *reinterpret_cast<const size_t*>(
6919+
FIELD_ADDR_CONST(this, kAllocationLengthOffset));
6920+
}
6921+
6922+
void JSArrayBuffer::set_allocation_length(size_t value) {
6923+
(*reinterpret_cast<size_t*>(FIELD_ADDR(this, kAllocationLengthOffset))) =
6924+
value;
6925+
}
69076926

69086927
void JSArrayBuffer::set_bit_field(uint32_t bits) {
69096928
if (kInt32Size != kPointerSize) {

src/objects.cc

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19584,8 +19584,10 @@ Handle<String> JSMessageObject::GetSourceLine() const {
1958419584
void JSArrayBuffer::Neuter() {
1958519585
CHECK(is_neuterable());
1958619586
CHECK(is_external());
19587-
set_backing_store(NULL);
19587+
set_backing_store(nullptr);
1958819588
set_byte_length(Smi::kZero);
19589+
set_allocation_base(nullptr);
19590+
set_allocation_length(0);
1958919591
set_was_neutered(true);
1959019592
// Invalidate the neutering protector.
1959119593
Isolate* const isolate = GetIsolate();
@@ -19594,10 +19596,25 @@ void JSArrayBuffer::Neuter() {
1959419596
}
1959519597
}
1959619598

19599+
void JSArrayBuffer::FreeBackingStore() {
19600+
using AllocationMode = ArrayBuffer::Allocator::AllocationMode;
19601+
const size_t length = allocation_length();
19602+
const AllocationMode mode = has_guard_region() ? AllocationMode::kReservation
19603+
: AllocationMode::kNormal;
19604+
GetIsolate()->array_buffer_allocator()->Free(allocation_base(), length, mode);
19605+
}
1959719606

1959819607
void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate,
1959919608
bool is_external, void* data, size_t allocated_length,
1960019609
SharedFlag shared) {
19610+
return Setup(array_buffer, isolate, is_external, data, allocated_length, data,
19611+
allocated_length, shared);
19612+
}
19613+
19614+
void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate,
19615+
bool is_external, void* allocation_base,
19616+
size_t allocation_length, void* data,
19617+
size_t byte_length, SharedFlag shared) {
1960119618
DCHECK(array_buffer->GetEmbedderFieldCount() ==
1960219619
v8::ArrayBuffer::kEmbedderFieldCount);
1960319620
for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
@@ -19608,16 +19625,19 @@ void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate,
1960819625
array_buffer->set_is_neuterable(shared == SharedFlag::kNotShared);
1960919626
array_buffer->set_is_shared(shared == SharedFlag::kShared);
1961019627

19611-
Handle<Object> byte_length =
19612-
isolate->factory()->NewNumberFromSize(allocated_length);
19613-
CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
19614-
array_buffer->set_byte_length(*byte_length);
19628+
Handle<Object> heap_byte_length =
19629+
isolate->factory()->NewNumberFromSize(byte_length);
19630+
CHECK(heap_byte_length->IsSmi() || heap_byte_length->IsHeapNumber());
19631+
array_buffer->set_byte_length(*heap_byte_length);
1961519632
// Initialize backing store at last to avoid handling of |JSArrayBuffers| that
1961619633
// are currently being constructed in the |ArrayBufferTracker|. The
1961719634
// registration method below handles the case of registering a buffer that has
1961819635
// already been promoted.
1961919636
array_buffer->set_backing_store(data);
1962019637

19638+
array_buffer->set_allocation_base(data);
19639+
array_buffer->set_allocation_length(allocation_length);
19640+
1962119641
if (data && !is_external) {
1962219642
isolate->heap()->RegisterNewArrayBuffer(*array_buffer);
1962319643
}
@@ -19658,8 +19678,9 @@ bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer,
1965819678
data = NULL;
1965919679
}
1966019680

19661-
JSArrayBuffer::Setup(array_buffer, isolate, false, data, allocated_length,
19662-
shared);
19681+
const bool is_external = false;
19682+
JSArrayBuffer::Setup(array_buffer, isolate, is_external, data,
19683+
allocated_length, shared);
1966319684
return true;
1966419685
}
1966519686

@@ -19690,6 +19711,8 @@ Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer(
1969019711
// already been promoted.
1969119712
buffer->set_backing_store(backing_store);
1969219713
isolate->heap()->RegisterNewArrayBuffer(*buffer);
19714+
buffer->set_allocation_base(backing_store);
19715+
buffer->set_allocation_length(NumberToSize(buffer->byte_length()));
1969319716
memcpy(buffer->backing_store(),
1969419717
fixed_typed_array->DataPtr(),
1969519718
fixed_typed_array->DataSize());

0 commit comments

Comments
 (0)