Chromium Code Reviews| Index: src/wasm/wasm-module.cc |
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
| index 64f5663aeb55c0a1fa74914ab16a65f139943169..127e730eacc3777aa1606b99a70b5b5754b94985 100644 |
| --- a/src/wasm/wasm-module.cc |
| +++ b/src/wasm/wasm-module.cc |
| @@ -114,28 +114,62 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, |
| } |
| } |
| -Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) { |
| - if (size > (WasmModule::kV8MaxPages * 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, |
| + RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize())); |
| + |
| + 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* TryAllocateBackingStore(Isolate* isolate, size_t size, bool guard) { |
| + if (guard) { |
|
titzer
2016/11/07 19:54:53
I think this needs to be TARGET_ARCH.
How about g
Eric Holk
2016/11/08 23:58:15
Done. I also updated the help text for --wasm_guar
|
| +#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 made inaccessible. |
| + const size_t alloc_size = |
| + RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize()); |
| + DCHECK_EQ(0, size % base::OS::CommitPageSize()); |
| + |
| + 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"); |
| + return nullptr; |
| +#endif |
| + } else { |
| + void* memory = isolate->array_buffer_allocator()->Allocate(size); |
| + return memory; |
| + } |
| } |
| void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, |
| @@ -631,6 +665,50 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { |
| } // namespace |
| +Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, |
| + bool enable_guard_regions) { |
|
titzer
2016/11/07 19:54:52
I like this name better. Can we make the header fi
Eric Holk
2016/11/08 23:58:15
Done.
|
| + if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) { |
| + // TODO(titzer): lift restriction on maximum memory allocated here. |
| + return Handle<JSArrayBuffer>::null(); |
| + } |
| + |
| + void* memory = TryAllocateBackingStore(isolate, size, enable_guard_regions); |
| + bool is_external = enable_guard_regions; |
|
titzer
2016/11/07 19:54:52
What about making is_external a return value from
Eric Holk
2016/11/08 23:58:15
Done, although it seems like V8_TARGET_ARCH_64_BIT
|
| + |
| + 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(enable_guard_regions); |
| + |
| +#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: |
| @@ -1076,8 +1154,9 @@ class WasmInstanceBuilder { |
| MaybeHandle<JSArrayBuffer> old_globals; |
| uint32_t globals_size = module_->globals_size; |
| if (globals_size > 0) { |
| + const bool enable_guard_regions = false; |
| Handle<JSArrayBuffer> global_buffer = |
| - NewArrayBuffer(isolate_, globals_size); |
| + NewArrayBuffer(isolate_, globals_size, enable_guard_regions); |
| globals_ = global_buffer; |
| if (globals_.is_null()) { |
| thrower_->RangeError("Out of memory: wasm globals"); |
| @@ -1128,6 +1207,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 |
| @@ -1579,8 +1661,9 @@ class WasmInstanceBuilder { |
| thrower_->RangeError("Out of memory: wasm memory too large"); |
| return Handle<JSArrayBuffer>::null(); |
| } |
| - Handle<JSArrayBuffer> mem_buffer = |
| - NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize); |
| + const bool enable_guard_regions = FLAG_wasm_guard_pages; |
| + Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer( |
| + isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions); |
| if (mem_buffer.is_null()) { |
| thrower_->RangeError("Out of memory: wasm memory"); |
| @@ -2141,16 +2224,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()) { |
|
titzer
2016/11/07 19:54:53
How about !old_buffer.is_null() and old_buffer.has
Eric Holk
2016/11/08 23:58:15
Done.
|
| + base::OS::Unprotect(old_buffer->backing_store(), new_size); |
|
titzer
2016/11/07 19:54:53
Add a comment that we aren't moving the memory her
Eric Holk
2016/11/08 23:58:15
Done.
|
| + 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 enable_guard_regions = FLAG_wasm_guard_pages; |
| + buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions); |
| + 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)) { |