Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Side by Side Diff: src/wasm/wasm-module.cc

Issue 2396433008: [wasm] Add guard regions to end of WebAssembly.Memory buffers (Closed)
Patch Set: Code review feedback Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <memory> 5 #include <memory>
6 6
7 #include "src/base/atomic-utils.h" 7 #include "src/base/atomic-utils.h"
8 #include "src/code-stubs.h" 8 #include "src/code-stubs.h"
9 9
10 #include "src/macro-assembler.h" 10 #include "src/macro-assembler.h"
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, 79 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
80 Handle<Object> new_ref) { 80 Handle<Object> new_ref) {
81 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done(); 81 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done();
82 it.next()) { 82 it.next()) {
83 if (it.rinfo()->target_object() == *old_ref) { 83 if (it.rinfo()->target_object() == *old_ref) {
84 it.rinfo()->set_target_object(*new_ref); 84 it.rinfo()->set_target_object(*new_ref);
85 } 85 }
86 } 86 }
87 } 87 }
88 88
89 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) { 89 #if V8_HOST_ARCH_64_BIT
90 if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { 90 static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) {
91 // TODO(titzer): lift restriction on maximum memory allocated here. 91 JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter());
92 return Handle<JSArrayBuffer>::null(); 92 JSArrayBuffer* buffer = *p;
93 }
94 void* memory = isolate->array_buffer_allocator()->Allocate(size);
95 if (memory == nullptr) {
96 return Handle<JSArrayBuffer>::null();
97 }
98 93
99 #if DEBUG 94 void* memory = buffer->backing_store();
100 // Double check the API allocator actually zero-initialized the memory. 95 base::OS::Free(memory, kWasmMaxHeapOffset);
101 const byte* bytes = reinterpret_cast<const byte*>(memory); 96
102 for (size_t i = 0; i < size; ++i) { 97 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
103 DCHECK_EQ(0, bytes[i]); 98 -buffer->byte_length()->Number());
104 } 99
100 GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
101 }
105 #endif 102 #endif
106 103
107 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); 104 void* AllocateBackingStore(Isolate* isolate, size_t size, bool guard) {
108 JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size)); 105 if (guard) {
109 buffer->set_is_neuterable(false); 106 #if V8_HOST_ARCH_64_BIT
110 return buffer; 107 void* memory;
108 // TODO(eholk): On Windows we want to make sure we don't commit the guard
109 // pages yet.
110
111 // We always allocate the largest possible offset into the heap, so the
112 // 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.
113 const size_t alloc_size = kWasmMaxHeapOffset;
114 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.
115
116 size_t allocated_size = 0;
117 const bool is_executable = false;
118 memory = base::OS::Allocate(alloc_size, &allocated_size, is_executable);
119 if (allocated_size < alloc_size) {
120 base::OS::Free(memory, allocated_size);
121 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.
122 }
123
124 if (memory == nullptr) {
125 return nullptr;
126 }
127
128 byte* bytes = reinterpret_cast<byte*>(memory);
129 base::OS::Guard(bytes + size, alloc_size - size);
130
131 reinterpret_cast<v8::Isolate*>(isolate)
132 ->AdjustAmountOfExternalAllocatedMemory(size);
133
134 return memory;
135 #else
136 DCHECK(false && "Guard pages are not supported on 32-bit systems");
137 #endif
138 } else {
139 void* memory = isolate->array_buffer_allocator()->Allocate(size);
140 return memory;
141 }
111 } 142 }
112 143
113 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, 144 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table,
114 Address old_start, Address start, 145 Address old_start, Address start,
115 uint32_t prev_size, uint32_t new_size) { 146 uint32_t prev_size, uint32_t new_size) {
116 for (int i = 0; i < code_table->length(); ++i) { 147 for (int i = 0; i < code_table->length(); ++i) {
117 DCHECK(code_table->get(i)->IsCode()); 148 DCHECK(code_table->get(i)->IsCode());
118 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); 149 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
119 AllowDeferredHandleDereference embedding_raw_address; 150 AllowDeferredHandleDereference embedding_raw_address;
120 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | 151 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) |
(...skipping 475 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0))); 627 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0)));
597 TRACE("}\n"); 628 TRACE("}\n");
598 } 629 }
599 compiled_module->reset_weak_owning_instance(); 630 compiled_module->reset_weak_owning_instance();
600 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); 631 GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
601 TRACE("}\n"); 632 TRACE("}\n");
602 } 633 }
603 634
604 } // namespace 635 } // namespace
605 636
637 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
638 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)
639 if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
640 // TODO(titzer): lift restriction on maximum memory allocated here.
641 return Handle<JSArrayBuffer>::null();
642 }
643
644 void* memory = AllocateBackingStore(isolate, size, guard);
645 bool is_external = guard;
646
647 if (memory == nullptr) {
648 return Handle<JSArrayBuffer>::null();
649 }
650
651 #if DEBUG
652 // Double check the API allocator actually zero-initialized the memory.
653 const byte* bytes = reinterpret_cast<const byte*>(memory);
654 for (size_t i = 0; i < size; ++i) {
655 DCHECK_EQ(0, bytes[i]);
656 }
657 #endif
658
659 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
660 JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
661 static_cast<int>(size));
662 buffer->set_is_neuterable(false);
663 buffer->set_has_guard_region(guard);
664
665 #if V8_HOST_ARCH_64_BIT
666 if (is_external) {
667 // We mark the buffer as external if we allocated it here with guard
668 // pages. That means we need to arrange for it to be freed.
669
670 // TODO(eholk): Finalizers may not run when the main thread is shutting
671 // down, which means we may leak memory here.
672 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
673 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
674 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
675 }
676 #endif
677
678 return buffer;
679 }
680
606 const char* wasm::SectionName(WasmSectionCode code) { 681 const char* wasm::SectionName(WasmSectionCode code) {
607 switch (code) { 682 switch (code) {
608 case kUnknownSectionCode: 683 case kUnknownSectionCode:
609 return "Unknown"; 684 return "Unknown";
610 case kTypeSectionCode: 685 case kTypeSectionCode:
611 return "Type"; 686 return "Type";
612 case kImportSectionCode: 687 case kImportSectionCode:
613 return "Import"; 688 return "Import";
614 case kFunctionSectionCode: 689 case kFunctionSectionCode:
615 return "Function"; 690 return "Function";
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
947 instance->SetInternalField(kWasmMemObject, 1022 instance->SetInternalField(kWasmMemObject,
948 isolate_->heap()->undefined_value()); 1023 isolate_->heap()->undefined_value());
949 1024
950 //-------------------------------------------------------------------------- 1025 //--------------------------------------------------------------------------
951 // Set up the globals for the new instance. 1026 // Set up the globals for the new instance.
952 //-------------------------------------------------------------------------- 1027 //--------------------------------------------------------------------------
953 MaybeHandle<JSArrayBuffer> old_globals; 1028 MaybeHandle<JSArrayBuffer> old_globals;
954 MaybeHandle<JSArrayBuffer> globals; 1029 MaybeHandle<JSArrayBuffer> globals;
955 uint32_t globals_size = module_->globals_size; 1030 uint32_t globals_size = module_->globals_size;
956 if (globals_size > 0) { 1031 if (globals_size > 0) {
1032 const bool guard = false;
957 Handle<JSArrayBuffer> global_buffer = 1033 Handle<JSArrayBuffer> global_buffer =
958 NewArrayBuffer(isolate_, globals_size); 1034 NewArrayBuffer(isolate_, globals_size, guard);
959 globals = global_buffer; 1035 globals = global_buffer;
960 if (globals.is_null()) { 1036 if (globals.is_null()) {
961 thrower_->RangeError("Out of memory: wasm globals"); 1037 thrower_->RangeError("Out of memory: wasm globals");
962 return nothing; 1038 return nothing;
963 } 1039 }
964 Address old_address = owner.is_null() 1040 Address old_address = owner.is_null()
965 ? nullptr 1041 ? nullptr
966 : GetGlobalStartAddressFromCodeTemplate( 1042 : GetGlobalStartAddressFromCodeTemplate(
967 isolate_->heap()->undefined_value(), 1043 isolate_->heap()->undefined_value(),
968 JSObject::cast(*owner.ToHandleChecked())); 1044 JSObject::cast(*owner.ToHandleChecked()));
(...skipping 18 matching lines...) Expand all
987 //-------------------------------------------------------------------------- 1063 //--------------------------------------------------------------------------
988 MaybeHandle<JSArrayBuffer> old_memory; 1064 MaybeHandle<JSArrayBuffer> old_memory;
989 1065
990 uint32_t min_mem_pages = module_->min_mem_pages; 1066 uint32_t min_mem_pages = module_->min_mem_pages;
991 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); 1067 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages);
992 // TODO(wasm): re-enable counter for max_mem_pages when we use that field. 1068 // TODO(wasm): re-enable counter for max_mem_pages when we use that field.
993 1069
994 if (!memory_.is_null()) { 1070 if (!memory_.is_null()) {
995 // Set externally passed ArrayBuffer non neuterable. 1071 // Set externally passed ArrayBuffer non neuterable.
996 memory_->set_is_neuterable(false); 1072 memory_->set_is_neuterable(false);
1073
1074 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
1075 memory_->has_guard_region());
997 } else if (min_mem_pages > 0) { 1076 } else if (min_mem_pages > 0) {
998 memory_ = AllocateMemory(min_mem_pages); 1077 memory_ = AllocateMemory(min_mem_pages);
999 if (memory_.is_null()) return nothing; // failed to allocate memory 1078 if (memory_.is_null()) return nothing; // failed to allocate memory
1000 } 1079 }
1001 1080
1002 if (!memory_.is_null()) { 1081 if (!memory_.is_null()) {
1003 instance->SetInternalField(kWasmMemArrayBuffer, *memory_); 1082 instance->SetInternalField(kWasmMemArrayBuffer, *memory_);
1004 Address mem_start = static_cast<Address>(memory_->backing_store()); 1083 Address mem_start = static_cast<Address>(memory_->backing_store());
1005 uint32_t mem_size = 1084 uint32_t mem_size =
1006 static_cast<uint32_t>(memory_->byte_length()->Number()); 1085 static_cast<uint32_t>(memory_->byte_length()->Number());
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after
1501 } 1580 }
1502 } 1581 }
1503 } 1582 }
1504 1583
1505 // Allocate memory for a module instance as a new JSArrayBuffer. 1584 // Allocate memory for a module instance as a new JSArrayBuffer.
1506 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { 1585 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) {
1507 if (min_mem_pages > WasmModule::kMaxMemPages) { 1586 if (min_mem_pages > WasmModule::kMaxMemPages) {
1508 thrower_->RangeError("Out of memory: wasm memory too large"); 1587 thrower_->RangeError("Out of memory: wasm memory too large");
1509 return Handle<JSArrayBuffer>::null(); 1588 return Handle<JSArrayBuffer>::null();
1510 } 1589 }
1590 const bool guard = FLAG_wasm_guard_pages;
1511 Handle<JSArrayBuffer> mem_buffer = 1591 Handle<JSArrayBuffer> mem_buffer =
1512 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize); 1592 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize, guard);
1513 1593
1514 if (mem_buffer.is_null()) { 1594 if (mem_buffer.is_null()) {
1515 thrower_->RangeError("Out of memory: wasm memory"); 1595 thrower_->RangeError("Out of memory: wasm memory");
1516 } 1596 }
1517 return mem_buffer; 1597 return mem_buffer;
1518 } 1598 }
1519 1599
1520 // Process the exports, creating wrappers for functions, tables, memories, 1600 // Process the exports, creating wrappers for functions, tables, memories,
1521 // and globals. 1601 // and globals.
1522 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, 1602 void ProcessExports(MaybeHandle<JSArrayBuffer> globals,
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after
1993 // "undefined" case above. 2073 // "undefined" case above.
1994 DCHECK_NOT_NULL(old_mem_start); 2074 DCHECK_NOT_NULL(old_mem_start);
1995 DCHECK(old_size + pages * WasmModule::kPageSize <= 2075 DCHECK(old_size + pages * WasmModule::kPageSize <=
1996 std::numeric_limits<uint32_t>::max()); 2076 std::numeric_limits<uint32_t>::max());
1997 new_size = old_size + pages * WasmModule::kPageSize; 2077 new_size = old_size + pages * WasmModule::kPageSize;
1998 } 2078 }
1999 2079
2000 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) { 2080 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) {
2001 return -1; 2081 return -1;
2002 } 2082 }
2003 Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size); 2083
2004 if (buffer.is_null()) return -1; 2084 Handle<JSArrayBuffer> buffer;
2005 Address new_mem_start = static_cast<Address>(buffer->backing_store()); 2085
2006 if (old_size != 0) { 2086 if (FLAG_wasm_guard_pages && !old_buffer.is_null()) {
2007 memcpy(new_mem_start, old_mem_start, old_size); 2087 base::OS::Unprotect(old_buffer->backing_store(), new_size);
2088 reinterpret_cast<v8::Isolate*>(isolate)
2089 ->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
2090 Handle<Object> new_size_object =
2091 isolate->factory()->NewNumberFromSize(new_size);
2092 old_buffer->set_byte_length(*new_size_object);
2093
2094 SetInstanceMemory(instance, *old_buffer);
2095 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
2096 RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start,
2097 old_size, new_size);
2098 buffer = old_buffer;
2099 } else {
2100 const bool guard = FLAG_wasm_guard_pages;
2101 buffer = NewArrayBuffer(isolate, new_size, guard);
2102 if (buffer.is_null()) return -1;
2103 Address new_mem_start = static_cast<Address>(buffer->backing_store());
2104 if (old_size != 0) {
2105 memcpy(new_mem_start, old_mem_start, old_size);
2106 }
2107 SetInstanceMemory(instance, *buffer);
2108 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
2109 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
2110 old_size, new_size);
2008 } 2111 }
2009 SetInstanceMemory(instance, *buffer); 2112
2010 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
2011 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
2012 old_size, new_size);
2013 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), 2113 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
2014 isolate); 2114 isolate);
2015 if (!memory_object->IsUndefined(isolate)) { 2115 if (!memory_object->IsUndefined(isolate)) {
2016 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer); 2116 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer);
2017 } 2117 }
2018 2118
2019 DCHECK(old_size % WasmModule::kPageSize == 0); 2119 DCHECK(old_size % WasmModule::kPageSize == 0);
2020 return (old_size / WasmModule::kPageSize); 2120 return (old_size / WasmModule::kPageSize);
2021 } 2121 }
2022 2122
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
2091 CHECK_NOT_NULL(result.val); 2191 CHECK_NOT_NULL(result.val);
2092 module = const_cast<WasmModule*>(result.val); 2192 module = const_cast<WasmModule*>(result.val);
2093 } 2193 }
2094 2194
2095 Handle<WasmModuleWrapper> module_wrapper = 2195 Handle<WasmModuleWrapper> module_wrapper =
2096 WasmModuleWrapper::New(isolate, module); 2196 WasmModuleWrapper::New(isolate, module);
2097 2197
2098 compiled_module->set_module_wrapper(module_wrapper); 2198 compiled_module->set_module_wrapper(module_wrapper);
2099 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); 2199 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
2100 } 2200 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698