Chromium Code Reviews| Index: src/wasm/wasm-module.cc |
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
| index 4aa5445b49bc537b92e1f0bf7cb574e258e013ad..f481e353a07a9239e36d9cd7300816530e4b083e 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. |
|
Mircea Trofin
2016/10/28 22:16:29
can be made
Eric Holk
2016/10/29 00:04:30
Done.
|
| + const size_t alloc_size = kWasmMaxHeapOffset; |
| + DCHECK(size % base::OS::CommitPageSize() == 0); |
|
Mircea Trofin
2016/10/28 22:16:29
DCHECK_EQ(0, size % etc)
Eric Holk
2016/10/29 00:04:30
Done.
|
| + |
| + 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; |
|
Mircea Trofin
2016/10/28 22:16:29
because it may fail, perhaps name the API TryAlloc
Eric Holk
2016/10/29 00:04:30
Done.
|
| + } |
| + |
| + 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, |
| @@ -603,6 +634,50 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { |
| } // namespace |
| +Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, |
| + bool guard) { |
|
Mircea Trofin
2016/10/28 22:16:29
guard->enable_guard_regions
also, if this flag is
Eric Holk
2016/10/29 00:04:30
Done.
Yes (at least on 64-bit platforms)
|
| + 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: |
| @@ -954,8 +1029,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"); |
| @@ -994,6 +1070,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 || |
|
Mircea Trofin
2016/10/28 22:16:29
asmjs, not wasm?
Eric Holk
2016/10/29 00:04:30
Yes. For AsmJs origin we still do the bounds check
|
| + 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 |
| @@ -1508,8 +1587,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"); |
| @@ -2000,16 +2080,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); |
|
Mircea Trofin
2016/10/28 22:16:29
would it make sense to add this API to the interna
Eric Holk
2016/10/29 00:04:30
Perhaps, but I'd rather leave it how it is. Adjust
Michael Lippautz
2016/10/29 07:39:19
+1 for leaving now. This API is used by several co
|
| + 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)) { |