1 // Copyright 2017 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_WASM_WASM_MEMORY_H_ 6 #define V8_WASM_WASM_MEMORY_H_ 7 8 #include <atomic> 9 #include <unordered_map> 10 11 #include "src/base/platform/mutex.h" 12 #include "src/flags.h" 13 #include "src/handles.h" 14 #include "src/objects/js-array-buffer.h" 15 16 namespace v8 { 17 namespace internal { 18 namespace wasm { 19 20 // The {WasmMemoryTracker} tracks reservations and allocations for wasm memory 21 // and wasm code. There is an upper limit on the total reserved memory which is 22 // checked by this class. Allocations are stored so we can look them up when an 23 // array buffer dies and figure out the reservation and allocation bounds for 24 // that buffer. 25 class WasmMemoryTracker { 26 public: WasmMemoryTracker()27 WasmMemoryTracker() {} 28 V8_EXPORT_PRIVATE ~WasmMemoryTracker(); 29 30 // ReserveAddressSpace attempts to increase the reserved address space counter 31 // by {num_bytes}. Returns true if successful (meaning it is okay to go ahead 32 // and reserve {num_bytes} bytes), false otherwise. 33 bool ReserveAddressSpace(size_t num_bytes); 34 35 void RegisterAllocation(Isolate* isolate, void* allocation_base, 36 size_t allocation_length, void* buffer_start, 37 size_t buffer_length); 38 39 struct AllocationData { 40 void* allocation_base = nullptr; 41 size_t allocation_length = 0; 42 void* buffer_start = nullptr; 43 size_t buffer_length = 0; 44 45 private: 46 AllocationData() = default; AllocationDataAllocationData47 AllocationData(void* allocation_base, size_t allocation_length, 48 void* buffer_start, size_t buffer_length) 49 : allocation_base(allocation_base), 50 allocation_length(allocation_length), 51 buffer_start(buffer_start), 52 buffer_length(buffer_length) { 53 DCHECK_LE(reinterpret_cast<uintptr_t>(allocation_base), 54 reinterpret_cast<uintptr_t>(buffer_start)); 55 DCHECK_GE( 56 reinterpret_cast<uintptr_t>(allocation_base) + allocation_length, 57 reinterpret_cast<uintptr_t>(buffer_start)); 58 DCHECK_GE( 59 reinterpret_cast<uintptr_t>(allocation_base) + allocation_length, 60 reinterpret_cast<uintptr_t>(buffer_start) + buffer_length); 61 } 62 63 friend WasmMemoryTracker; 64 }; 65 66 // Decreases the amount of reserved address space. 67 void ReleaseReservation(size_t num_bytes); 68 69 // Removes an allocation from the tracker. 70 AllocationData ReleaseAllocation(Isolate* isolate, const void* buffer_start); 71 72 bool IsWasmMemory(const void* buffer_start); 73 74 // Returns whether the given buffer is a Wasm memory with guard regions large 75 // enough to safely use trap handlers. 76 bool HasFullGuardRegions(const void* buffer_start); 77 78 // Returns a pointer to a Wasm buffer's allocation data, or nullptr if the 79 // buffer is not tracked. 80 const AllocationData* FindAllocationData(const void* buffer_start); 81 82 // Checks if a buffer points to a Wasm memory and if so does any necessary 83 // work to reclaim the buffer. If this function returns false, the caller must 84 // free the buffer manually. 85 bool FreeMemoryIfIsWasmMemory(Isolate* isolate, const void* buffer_start); 86 87 // Allocation results are reported to UMA 88 // 89 // See wasm_memory_allocation_result in counters.h 90 enum class AllocationStatus { 91 kSuccess, // Succeeded on the first try 92 93 kSuccessAfterRetry, // Succeeded after garbage collection 94 95 kAddressSpaceLimitReachedFailure, // Failed because Wasm is at its address 96 // space limit 97 98 kOtherFailure // Failed for an unknown reason 99 }; 100 101 private: 102 void AddAddressSpaceSample(Isolate* isolate); 103 104 // Clients use a two-part process. First they "reserve" the address space, 105 // which signifies an intent to actually allocate it. This determines whether 106 // doing the allocation would put us over our limit. Once there is a 107 // reservation, clients can do the allocation and register the result. 108 // 109 // We should always have: 110 // allocated_address_space_ <= reserved_address_space_ <= kAddressSpaceLimit 111 std::atomic<size_t> reserved_address_space_{0}; 112 113 // Used to protect access to the allocated address space counter and 114 // allocation map. This is needed because Wasm memories can be freed on 115 // another thread by the ArrayBufferTracker. 116 base::Mutex mutex_; 117 118 size_t allocated_address_space_ = 0; 119 120 // Track Wasm memory allocation information. This is keyed by the start of the 121 // buffer, rather than by the start of the allocation. 122 std::unordered_map<const void*, AllocationData> allocations_; 123 124 DISALLOW_COPY_AND_ASSIGN(WasmMemoryTracker); 125 }; 126 127 // Attempts to allocate an array buffer with guard regions suitable for trap 128 // handling. If address space is not available, it will return a buffer with 129 // mini-guards that will require bounds checks. 130 MaybeHandle<JSArrayBuffer> NewArrayBuffer( 131 Isolate*, size_t size, SharedFlag shared = SharedFlag::kNotShared); 132 133 Handle<JSArrayBuffer> SetupArrayBuffer( 134 Isolate*, void* backing_store, size_t size, bool is_external, 135 SharedFlag shared = SharedFlag::kNotShared); 136 137 void DetachMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer, 138 bool free_memory); 139 140 } // namespace wasm 141 } // namespace internal 142 } // namespace v8 143 144 #endif // V8_WASM_WASM_MEMORY_H_ 145