| Index: src/wasm/wasm-module.cc
|
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
|
| index 7bc382600051d64fd7ecb84818174cf2edca4ba6..a3e621d7856d439d221cfbb508750bf95e6f1a70 100644
|
| --- a/src/wasm/wasm-module.cc
|
| +++ b/src/wasm/wasm-module.cc
|
| @@ -86,28 +86,59 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
|
| }
|
| }
|
|
|
| -Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
|
| - if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
|
| - // TODO(titzer): lift restriction on maximum memory allocated here.
|
| - return Handle<JSArrayBuffer>::null();
|
| - }
|
| - void* memory = isolate->array_buffer_allocator()->Allocate(size);
|
| - if (memory == nullptr) {
|
| - return Handle<JSArrayBuffer>::null();
|
| - }
|
| +#if V8_HOST_ARCH_64_BIT
|
| +static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
| + JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter());
|
| + JSArrayBuffer* buffer = *p;
|
|
|
| -#if DEBUG
|
| - // Double check the API allocator actually zero-initialized the memory.
|
| - const byte* bytes = reinterpret_cast<const byte*>(memory);
|
| - for (size_t i = 0; i < size; ++i) {
|
| - DCHECK_EQ(0, bytes[i]);
|
| - }
|
| + void* memory = buffer->backing_store();
|
| + base::OS::Free(memory, kWasmMaxHeapOffset);
|
| +
|
| + data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
|
| + -buffer->byte_length()->Number());
|
| +
|
| + GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
|
| +}
|
| #endif
|
|
|
| - Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
|
| - JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size));
|
| - buffer->set_is_neuterable(false);
|
| - return buffer;
|
| +void* AllocateBackingStore(Isolate* isolate, size_t size, bool guard) {
|
| + if (guard) {
|
| +#if V8_HOST_ARCH_64_BIT
|
| + void* memory;
|
| + // TODO(eholk): On Windows we want to make sure we don't commit the guard
|
| + // pages yet.
|
| +
|
| + // We always allocate the largest possible offset into the heap, so the
|
| + // addressable memory after the guard page can be make inaccessible.
|
| + const size_t alloc_size = kWasmMaxHeapOffset;
|
| + DCHECK(size % base::OS::CommitPageSize() == 0);
|
| +
|
| + size_t allocated_size = 0;
|
| + const bool is_executable = false;
|
| + memory = base::OS::Allocate(alloc_size, &allocated_size, is_executable);
|
| + if (allocated_size < alloc_size) {
|
| + base::OS::Free(memory, allocated_size);
|
| + return nullptr;
|
| + }
|
| +
|
| + if (memory == nullptr) {
|
| + return nullptr;
|
| + }
|
| +
|
| + byte* bytes = reinterpret_cast<byte*>(memory);
|
| + base::OS::Guard(bytes + size, alloc_size - size);
|
| +
|
| + reinterpret_cast<v8::Isolate*>(isolate)
|
| + ->AdjustAmountOfExternalAllocatedMemory(size);
|
| +
|
| + return memory;
|
| +#else
|
| + DCHECK(false && "Guard pages are not supported on 32-bit systems");
|
| +#endif
|
| + } else {
|
| + void* memory = isolate->array_buffer_allocator()->Allocate(size);
|
| + return memory;
|
| + }
|
| }
|
|
|
| void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table,
|
| @@ -602,6 +633,50 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
|
|
| } // namespace
|
|
|
| +Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
|
| + bool guard) {
|
| + if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
|
| + // TODO(titzer): lift restriction on maximum memory allocated here.
|
| + return Handle<JSArrayBuffer>::null();
|
| + }
|
| +
|
| + void* memory = AllocateBackingStore(isolate, size, guard);
|
| + bool is_external = guard;
|
| +
|
| + if (memory == nullptr) {
|
| + return Handle<JSArrayBuffer>::null();
|
| + }
|
| +
|
| +#if DEBUG
|
| + // Double check the API allocator actually zero-initialized the memory.
|
| + const byte* bytes = reinterpret_cast<const byte*>(memory);
|
| + for (size_t i = 0; i < size; ++i) {
|
| + DCHECK_EQ(0, bytes[i]);
|
| + }
|
| +#endif
|
| +
|
| + Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
|
| + JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
|
| + static_cast<int>(size));
|
| + buffer->set_is_neuterable(false);
|
| + buffer->set_has_guard_region(guard);
|
| +
|
| +#if V8_HOST_ARCH_64_BIT
|
| + if (is_external) {
|
| + // We mark the buffer as external if we allocated it here with guard
|
| + // pages. That means we need to arrange for it to be freed.
|
| +
|
| + // TODO(eholk): Finalizers may not run when the main thread is shutting
|
| + // down, which means we may leak memory here.
|
| + Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
|
| + GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
|
| + &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
|
| + }
|
| +#endif
|
| +
|
| + return buffer;
|
| +}
|
| +
|
| const char* wasm::SectionName(WasmSectionCode code) {
|
| switch (code) {
|
| case kUnknownSectionCode:
|
| @@ -953,8 +1028,9 @@ class WasmInstanceBuilder {
|
| MaybeHandle<JSArrayBuffer> globals;
|
| uint32_t globals_size = module_->globals_size;
|
| if (globals_size > 0) {
|
| + const bool guard = false;
|
| Handle<JSArrayBuffer> global_buffer =
|
| - NewArrayBuffer(isolate_, globals_size);
|
| + NewArrayBuffer(isolate_, globals_size, guard);
|
| globals = global_buffer;
|
| if (globals.is_null()) {
|
| thrower_->RangeError("Out of memory: wasm globals");
|
| @@ -993,6 +1069,9 @@ class WasmInstanceBuilder {
|
| if (!memory_.is_null()) {
|
| // Set externally passed ArrayBuffer non neuterable.
|
| memory_->set_is_neuterable(false);
|
| +
|
| + DCHECK_IMPLIES(FLAG_wasm_guard_pages, module_->origin == kAsmJsOrigin ||
|
| + memory_->has_guard_region());
|
| } else if (min_mem_pages > 0) {
|
| memory_ = AllocateMemory(min_mem_pages);
|
| if (memory_.is_null()) return nothing; // failed to allocate memory
|
| @@ -1507,8 +1586,9 @@ class WasmInstanceBuilder {
|
| thrower_->RangeError("Out of memory: wasm memory too large");
|
| return Handle<JSArrayBuffer>::null();
|
| }
|
| + const bool guard = FLAG_wasm_guard_pages;
|
| Handle<JSArrayBuffer> mem_buffer =
|
| - NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize);
|
| + NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize, guard);
|
|
|
| if (mem_buffer.is_null()) {
|
| thrower_->RangeError("Out of memory: wasm memory");
|
| @@ -1999,16 +2079,36 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
|
| if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) {
|
| return -1;
|
| }
|
| - Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
|
| - if (buffer.is_null()) return -1;
|
| - Address new_mem_start = static_cast<Address>(buffer->backing_store());
|
| - if (old_size != 0) {
|
| - memcpy(new_mem_start, old_mem_start, old_size);
|
| +
|
| + Handle<JSArrayBuffer> buffer;
|
| +
|
| + if (FLAG_wasm_guard_pages && !old_buffer.is_null()) {
|
| + base::OS::Unprotect(old_buffer->backing_store(), new_size);
|
| + reinterpret_cast<v8::Isolate*>(isolate)
|
| + ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize);
|
| + Handle<Object> new_size_object =
|
| + isolate->factory()->NewNumberFromSize(new_size);
|
| + old_buffer->set_byte_length(*new_size_object);
|
| +
|
| + SetInstanceMemory(instance, *old_buffer);
|
| + Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
|
| + RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start,
|
| + old_size, new_size);
|
| + buffer = old_buffer;
|
| + } else {
|
| + const bool guard = FLAG_wasm_guard_pages;
|
| + buffer = NewArrayBuffer(isolate, new_size, guard);
|
| + if (buffer.is_null()) return -1;
|
| + Address new_mem_start = static_cast<Address>(buffer->backing_store());
|
| + if (old_size != 0) {
|
| + memcpy(new_mem_start, old_mem_start, old_size);
|
| + }
|
| + SetInstanceMemory(instance, *buffer);
|
| + Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
|
| + RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
|
| + old_size, new_size);
|
| }
|
| - SetInstanceMemory(instance, *buffer);
|
| - Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
|
| - RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
|
| - old_size, new_size);
|
| +
|
| Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
|
| isolate);
|
| if (!memory_object->IsUndefined(isolate)) {
|
|
|