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

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

Issue 2653183003: [wasm] Memory buffer should be detached after Memory.Grow (Closed)
Patch Set: Ben's review Created 3 years, 11 months 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
« no previous file with comments | « no previous file | test/mjsunit/wasm/import-memory.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/assembler-inl.h" 7 #include "src/assembler-inl.h"
8 #include "src/base/adapters.h" 8 #include "src/base/adapters.h"
9 #include "src/base/atomic-utils.h" 9 #include "src/base/atomic-utils.h"
10 #include "src/code-stubs.h" 10 #include "src/code-stubs.h"
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 it.rinfo()->set_target_object(*new_ref); 54 it.rinfo()->set_target_object(*new_ref);
55 } 55 }
56 } 56 }
57 } 57 }
58 58
59 static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) { 59 static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) {
60 DisallowHeapAllocation no_gc; 60 DisallowHeapAllocation no_gc;
61 JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter()); 61 JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter());
62 JSArrayBuffer* buffer = *p; 62 JSArrayBuffer* buffer = *p;
63 63
64 void* memory = buffer->backing_store(); 64 if (!buffer->was_neutered()) {
65 base::OS::Free(memory, 65 void* memory = buffer->backing_store();
66 RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize())); 66 DCHECK(memory != nullptr);
67 base::OS::Free(memory,
68 RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize()));
67 69
68 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory( 70 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
69 -buffer->byte_length()->Number()); 71 -buffer->byte_length()->Number());
72 }
70 73
71 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); 74 GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
72 } 75 }
73 76
74 #if V8_TARGET_ARCH_64_BIT 77 #if V8_TARGET_ARCH_64_BIT
75 const bool kGuardRegionsSupported = true; 78 const bool kGuardRegionsSupported = true;
76 #else 79 #else
77 const bool kGuardRegionsSupported = false; 80 const bool kGuardRegionsSupported = false;
78 #endif 81 #endif
79 82
(...skipping 669 matching lines...) Expand 10 before | Expand all | Expand 10 after
749 DCHECK(name_chars >= 0 && name_chars < kBufferSize); 752 DCHECK(name_chars >= 0 && name_chars < kBufferSize);
750 MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( 753 MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
751 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), 754 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
752 TENURED); 755 TENURED);
753 script->set_name(*name_str.ToHandleChecked()); 756 script->set_name(*name_str.ToHandleChecked());
754 757
755 return script; 758 return script;
756 } 759 }
757 } // namespace 760 } // namespace
758 761
762 Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store,
763 size_t size, bool is_external,
764 bool enable_guard_regions) {
765 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
766 JSArrayBuffer::Setup(buffer, isolate, is_external, backing_store,
767 static_cast<int>(size));
768 buffer->set_is_neuterable(false);
769 buffer->set_has_guard_region(enable_guard_regions);
770
771 if (is_external) {
772 // We mark the buffer as external if we allocated it here with guard
773 // pages. That means we need to arrange for it to be freed.
774
775 // TODO(eholk): Finalizers may not run when the main thread is shutting
776 // down, which means we may leak memory here.
777 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
778 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
779 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
780 }
781 return buffer;
782 }
783
759 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, 784 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
760 bool enable_guard_regions) { 785 bool enable_guard_regions) {
761 if (size > (FLAG_wasm_max_mem_pages * WasmModule::kPageSize)) { 786 if (size > (FLAG_wasm_max_mem_pages * WasmModule::kPageSize)) {
762 // TODO(titzer): lift restriction on maximum memory allocated here. 787 // TODO(titzer): lift restriction on maximum memory allocated here.
763 return Handle<JSArrayBuffer>::null(); 788 return Handle<JSArrayBuffer>::null();
764 } 789 }
765 790
766 enable_guard_regions = enable_guard_regions && kGuardRegionsSupported; 791 enable_guard_regions = enable_guard_regions && kGuardRegionsSupported;
767 792
768 bool is_external; // Set by TryAllocateBackingStore 793 bool is_external; // Set by TryAllocateBackingStore
769 void* memory = 794 void* memory =
770 TryAllocateBackingStore(isolate, size, enable_guard_regions, is_external); 795 TryAllocateBackingStore(isolate, size, enable_guard_regions, is_external);
771 796
772 if (memory == nullptr) { 797 if (memory == nullptr) {
773 return Handle<JSArrayBuffer>::null(); 798 return Handle<JSArrayBuffer>::null();
774 } 799 }
775 800
776 #if DEBUG 801 #if DEBUG
777 // Double check the API allocator actually zero-initialized the memory. 802 // Double check the API allocator actually zero-initialized the memory.
778 const byte* bytes = reinterpret_cast<const byte*>(memory); 803 const byte* bytes = reinterpret_cast<const byte*>(memory);
779 for (size_t i = 0; i < size; ++i) { 804 for (size_t i = 0; i < size; ++i) {
780 DCHECK_EQ(0, bytes[i]); 805 DCHECK_EQ(0, bytes[i]);
781 } 806 }
782 #endif 807 #endif
783 808
784 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); 809 return SetupArrayBuffer(isolate, memory, size, is_external,
785 JSArrayBuffer::Setup(buffer, isolate, is_external, memory, 810 enable_guard_regions);
786 static_cast<int>(size));
787 buffer->set_is_neuterable(false);
788 buffer->set_has_guard_region(enable_guard_regions);
789
790 if (is_external) {
791 // We mark the buffer as external if we allocated it here with guard
792 // pages. That means we need to arrange for it to be freed.
793
794 // TODO(eholk): Finalizers may not run when the main thread is shutting
795 // down, which means we may leak memory here.
796 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
797 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
798 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
799 }
800
801 return buffer;
802 } 811 }
803 812
804 const char* wasm::SectionName(WasmSectionCode code) { 813 const char* wasm::SectionName(WasmSectionCode code) {
805 switch (code) { 814 switch (code) {
806 case kUnknownSectionCode: 815 case kUnknownSectionCode:
807 return "Unknown"; 816 return "Unknown";
808 case kTypeSectionCode: 817 case kTypeSectionCode:
809 return "Type"; 818 return "Type";
810 case kImportSectionCode: 819 case kImportSectionCode:
811 return "Import"; 820 return "Import";
(...skipping 1530 matching lines...) Expand 10 before | Expand all | Expand 10 after
2342 old_size = old_buffer->byte_length()->Number(); 2351 old_size = old_buffer->byte_length()->Number();
2343 } 2352 }
2344 DCHECK(old_size + pages * WasmModule::kPageSize <= 2353 DCHECK(old_size + pages * WasmModule::kPageSize <=
2345 std::numeric_limits<uint32_t>::max()); 2354 std::numeric_limits<uint32_t>::max());
2346 uint32_t new_size = old_size + pages * WasmModule::kPageSize; 2355 uint32_t new_size = old_size + pages * WasmModule::kPageSize;
2347 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size || 2356 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size ||
2348 FLAG_wasm_max_mem_pages * WasmModule::kPageSize < new_size) { 2357 FLAG_wasm_max_mem_pages * WasmModule::kPageSize < new_size) {
2349 return Handle<JSArrayBuffer>::null(); 2358 return Handle<JSArrayBuffer>::null();
2350 } 2359 }
2351 2360
2352 Handle<JSArrayBuffer> new_buffer; 2361 // TODO(gdeepti): Change the protection here instead of allocating a new
2353 if (!old_buffer.is_null() && old_buffer->has_guard_region()) { 2362 // buffer before guard regions are turned on, see issue #5886.
2354 // We don't move the backing store, we simply change the protection to make 2363 const bool enable_guard_regions =
2355 // more of it accessible. 2364 !old_buffer.is_null() && old_buffer->has_guard_region();
2356 base::OS::Unprotect(old_buffer->backing_store(), new_size); 2365 Handle<JSArrayBuffer> new_buffer =
2357 reinterpret_cast<v8::Isolate*>(isolate) 2366 NewArrayBuffer(isolate, new_size, enable_guard_regions);
2358 ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize); 2367 if (new_buffer.is_null()) return new_buffer;
2359 Handle<Object> new_size_object = 2368 Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
2360 isolate->factory()->NewNumberFromSize(new_size); 2369 if (old_size != 0) {
2361 old_buffer->set_byte_length(*new_size_object); 2370 memcpy(new_mem_start, old_mem_start, old_size);
2362 new_buffer = old_buffer;
2363 } else {
2364 const bool enable_guard_regions = false;
2365 new_buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions);
2366 if (new_buffer.is_null()) return new_buffer;
2367 Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
2368 if (old_size != 0) {
2369 memcpy(new_mem_start, old_mem_start, old_size);
2370 }
2371 } 2371 }
2372 return new_buffer; 2372 return new_buffer;
2373 } 2373 }
2374 2374
2375 void UncheckedUpdateInstanceMemory(Isolate* isolate, 2375 void UncheckedUpdateInstanceMemory(Isolate* isolate,
2376 Handle<WasmInstanceObject> instance, 2376 Handle<WasmInstanceObject> instance,
2377 Address old_mem_start, uint32_t old_size) { 2377 Address old_mem_start, uint32_t old_size) {
2378 DCHECK(instance->has_memory_buffer()); 2378 DCHECK(instance->has_memory_buffer());
2379 Handle<JSArrayBuffer> new_buffer(instance->memory_buffer()); 2379 Handle<JSArrayBuffer> new_buffer(instance->memory_buffer());
2380 uint32_t new_size = new_buffer->byte_length()->Number(); 2380 uint32_t new_size = new_buffer->byte_length()->Number();
2381 DCHECK(new_size <= std::numeric_limits<uint32_t>::max()); 2381 DCHECK(new_size <= std::numeric_limits<uint32_t>::max());
2382 Address new_mem_start = static_cast<Address>(new_buffer->backing_store()); 2382 Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
2383 DCHECK_NOT_NULL(new_mem_start); 2383 DCHECK_NOT_NULL(new_mem_start);
2384 Handle<FixedArray> code_table = instance->compiled_module()->code_table(); 2384 Handle<FixedArray> code_table = instance->compiled_module()->code_table();
2385 RelocateMemoryReferencesInCode( 2385 RelocateMemoryReferencesInCode(
2386 code_table, instance->compiled_module()->module()->num_imported_functions, 2386 code_table, instance->compiled_module()->module()->num_imported_functions,
2387 old_mem_start, new_mem_start, old_size, new_size); 2387 old_mem_start, new_mem_start, old_size, new_size);
2388 } 2388 }
2389 2389
2390 void DetachArrayBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer) {
2391 const bool has_guard_regions =
2392 (!buffer.is_null() && buffer->has_guard_region());
2393 void* backing_store = buffer->backing_store();
2394 if (backing_store != nullptr) {
2395 DCHECK(!buffer->is_neuterable());
2396 int64_t byte_length = NumberToSize(buffer->byte_length());
2397 buffer->set_is_neuterable(true);
2398 if (!has_guard_regions) {
2399 buffer->set_is_external(true);
2400 isolate->heap()->UnregisterArrayBuffer(*buffer);
2401 }
2402 buffer->Neuter();
2403 if (!has_guard_regions) {
2404 isolate->array_buffer_allocator()->Free(backing_store, byte_length);
2405 } else {
2406 base::OS::Free(backing_store, RoundUp(i::wasm::kWasmMaxHeapOffset,
2407 base::OS::CommitPageSize()));
2408 reinterpret_cast<v8::Isolate*>(isolate)
2409 ->AdjustAmountOfExternalAllocatedMemory(-byte_length);
2410 }
2411 }
2412 }
2413
2390 int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, 2414 int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
2391 Handle<WasmMemoryObject> receiver, 2415 Handle<WasmMemoryObject> receiver,
2392 uint32_t pages) { 2416 uint32_t pages) {
2393 DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver)); 2417 DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver));
2394 Handle<WasmMemoryObject> memory_object = 2418 Handle<WasmMemoryObject> memory_object =
2395 handle(WasmMemoryObject::cast(*receiver)); 2419 handle(WasmMemoryObject::cast(*receiver));
2396 MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer()); 2420 MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
2397 Handle<JSArrayBuffer> old_buffer; 2421 Handle<JSArrayBuffer> old_buffer;
2398 uint32_t old_size = 0; 2422 uint32_t old_size = 0;
2399 Address old_mem_start = nullptr; 2423 Address old_mem_start = nullptr;
2400 if (memory_buffer.ToHandle(&old_buffer) && 2424 if (memory_buffer.ToHandle(&old_buffer) &&
2401 old_buffer->backing_store() != nullptr) { 2425 old_buffer->backing_store() != nullptr) {
2402 old_size = old_buffer->byte_length()->Number(); 2426 old_size = old_buffer->byte_length()->Number();
2403 old_mem_start = static_cast<Address>(old_buffer->backing_store()); 2427 old_mem_start = static_cast<Address>(old_buffer->backing_store());
2404 } 2428 }
2429 Handle<JSArrayBuffer> new_buffer;
2405 // Return current size if grow by 0 2430 // Return current size if grow by 0
2406 if (pages == 0) { 2431 if (pages == 0) {
2432 if (!old_buffer.is_null() && old_buffer->backing_store() != nullptr) {
2433 new_buffer = SetupArrayBuffer(isolate, old_buffer->backing_store(),
2434 old_size, old_buffer->is_external(),
2435 old_buffer->has_guard_region());
2436 memory_object->set_buffer(*new_buffer);
2437 old_buffer->set_is_neuterable(true);
2438 if (!old_buffer->has_guard_region()) {
2439 old_buffer->set_is_external(true);
2440 isolate->heap()->UnregisterArrayBuffer(*old_buffer);
2441 }
2442 // Neuter but don't free the memory because it is now being used by
2443 // new_buffer.
2444 old_buffer->Neuter();
2445 }
2407 DCHECK(old_size % WasmModule::kPageSize == 0); 2446 DCHECK(old_size % WasmModule::kPageSize == 0);
2408 return (old_size / WasmModule::kPageSize); 2447 return (old_size / WasmModule::kPageSize);
2409 } 2448 }
2410 Handle<JSArrayBuffer> new_buffer;
2411 if (!memory_object->has_instances_link()) { 2449 if (!memory_object->has_instances_link()) {
2412 // Memory object does not have an instance associated with it, just grow 2450 // Memory object does not have an instance associated with it, just grow
2413 uint32_t max_pages; 2451 uint32_t max_pages;
2414 if (memory_object->has_maximum_pages()) { 2452 if (memory_object->has_maximum_pages()) {
2415 max_pages = static_cast<uint32_t>(memory_object->maximum_pages()); 2453 max_pages = static_cast<uint32_t>(memory_object->maximum_pages());
2416 if (FLAG_wasm_max_mem_pages < max_pages) return -1; 2454 if (FLAG_wasm_max_mem_pages < max_pages) return -1;
2417 } else { 2455 } else {
2418 max_pages = FLAG_wasm_max_mem_pages; 2456 max_pages = FLAG_wasm_max_mem_pages;
2419 } 2457 }
2420 new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages); 2458 new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
(...skipping 16 matching lines...) Expand all
2437 while (instance_wrapper->has_next()) { 2475 while (instance_wrapper->has_next()) {
2438 instance_wrapper = instance_wrapper->next_wrapper(); 2476 instance_wrapper = instance_wrapper->next_wrapper();
2439 DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); 2477 DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
2440 Handle<WasmInstanceObject> instance = instance_wrapper->instance_object(); 2478 Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
2441 DCHECK(IsWasmInstance(*instance)); 2479 DCHECK(IsWasmInstance(*instance));
2442 SetInstanceMemory(instance, *new_buffer); 2480 SetInstanceMemory(instance, *new_buffer);
2443 UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); 2481 UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
2444 } 2482 }
2445 } 2483 }
2446 memory_object->set_buffer(*new_buffer); 2484 memory_object->set_buffer(*new_buffer);
2485 DetachArrayBuffer(isolate, old_buffer);
2447 DCHECK(old_size % WasmModule::kPageSize == 0); 2486 DCHECK(old_size % WasmModule::kPageSize == 0);
2448 return (old_size / WasmModule::kPageSize); 2487 return (old_size / WasmModule::kPageSize);
2449 } 2488 }
2450 2489
2451 int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance, 2490 int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
2452 uint32_t pages) { 2491 uint32_t pages) {
2453 if (!IsWasmInstance(*instance)) return -1; 2492 if (!IsWasmInstance(*instance)) return -1;
2454 if (pages == 0) return GetInstanceMemorySize(isolate, instance); 2493 if (pages == 0) return GetInstanceMemorySize(isolate, instance);
2455 Handle<WasmInstanceObject> instance_obj(WasmInstanceObject::cast(*instance)); 2494 Handle<WasmInstanceObject> instance_obj(WasmInstanceObject::cast(*instance));
2456 if (!instance_obj->has_memory_object()) { 2495 if (!instance_obj->has_memory_object()) {
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
2754 Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections); 2793 Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections);
2755 JSArray::SetContent(array_object, storage); 2794 JSArray::SetContent(array_object, storage);
2756 array_object->set_length(Smi::FromInt(num_custom_sections)); 2795 array_object->set_length(Smi::FromInt(num_custom_sections));
2757 2796
2758 for (int i = 0; i < num_custom_sections; i++) { 2797 for (int i = 0; i < num_custom_sections; i++) {
2759 storage->set(i, *matching_sections[i]); 2798 storage->set(i, *matching_sections[i]);
2760 } 2799 }
2761 2800
2762 return array_object; 2801 return array_object;
2763 } 2802 }
OLDNEW
« no previous file with comments | « no previous file | test/mjsunit/wasm/import-memory.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698