Line data Source code
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 : #include <unordered_set>
11 :
12 : #include "src/base/platform/mutex.h"
13 : #include "src/flags.h"
14 : #include "src/handles.h"
15 : #include "src/objects/js-array-buffer.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 : namespace wasm {
20 :
21 : // The {WasmMemoryTracker} tracks reservations and allocations for wasm memory
22 : // and wasm code. There is an upper limit on the total reserved memory which is
23 : // checked by this class. Allocations are stored so we can look them up when an
24 : // array buffer dies and figure out the reservation and allocation bounds for
25 : // that buffer.
26 : class WasmMemoryTracker {
27 : public:
28 180402 : WasmMemoryTracker() = default;
29 : V8_EXPORT_PRIVATE ~WasmMemoryTracker();
30 :
31 : // ReserveAddressSpace attempts to increase the reserved address space counter
32 : // by {num_bytes}. Returns true if successful (meaning it is okay to go ahead
33 : // and reserve {num_bytes} bytes), false otherwise.
34 : // Use {kSoftLimit} if you can implement a fallback which needs less reserved
35 : // memory.
36 : enum ReservationLimit { kSoftLimit, kHardLimit };
37 : bool ReserveAddressSpace(size_t num_bytes, ReservationLimit limit);
38 :
39 : void RegisterAllocation(Isolate* isolate, void* allocation_base,
40 : size_t allocation_length, void* buffer_start,
41 : size_t buffer_length);
42 :
43 : struct SharedMemoryObjectState {
44 : Handle<WasmMemoryObject> memory_object;
45 : Isolate* isolate;
46 :
47 : SharedMemoryObjectState() = default;
48 : SharedMemoryObjectState(Handle<WasmMemoryObject> memory_object,
49 : Isolate* isolate)
50 304 : : memory_object(memory_object), isolate(isolate) {}
51 : };
52 :
53 886020 : struct AllocationData {
54 : void* allocation_base = nullptr;
55 : size_t allocation_length = 0;
56 : void* buffer_start = nullptr;
57 : size_t buffer_length = 0;
58 : bool is_shared = false;
59 :
60 : // Track Wasm Memory instances across isolates, this is populated on
61 : // PostMessage using persistent handles for memory objects.
62 : std::vector<WasmMemoryTracker::SharedMemoryObjectState>
63 : memory_object_vector;
64 :
65 : private:
66 : AllocationData() = default;
67 : AllocationData(void* allocation_base, size_t allocation_length,
68 : void* buffer_start, size_t buffer_length)
69 : : allocation_base(allocation_base),
70 : allocation_length(allocation_length),
71 : buffer_start(buffer_start),
72 177204 : buffer_length(buffer_length) {
73 : DCHECK_LE(reinterpret_cast<uintptr_t>(allocation_base),
74 : reinterpret_cast<uintptr_t>(buffer_start));
75 : DCHECK_GE(
76 : reinterpret_cast<uintptr_t>(allocation_base) + allocation_length,
77 : reinterpret_cast<uintptr_t>(buffer_start));
78 : DCHECK_GE(
79 : reinterpret_cast<uintptr_t>(allocation_base) + allocation_length,
80 : reinterpret_cast<uintptr_t>(buffer_start) + buffer_length);
81 : }
82 :
83 : friend WasmMemoryTracker;
84 : };
85 :
86 : // Allow tests to allocate a backing store the same way as we do it for
87 : // WebAssembly memory. This is used in unit tests for trap handler to
88 : // generate the same signals/exceptions for invalid memory accesses as
89 : // we would get with WebAssembly memory.
90 : V8_EXPORT_PRIVATE void* TryAllocateBackingStoreForTesting(
91 : Heap* heap, size_t size, void** allocation_base,
92 : size_t* allocation_length);
93 :
94 : // Free memory allocated with TryAllocateBackingStoreForTesting.
95 : V8_EXPORT_PRIVATE void FreeBackingStoreForTesting(base::AddressRegion memory,
96 : void* buffer_start);
97 :
98 : // Decreases the amount of reserved address space.
99 : void ReleaseReservation(size_t num_bytes);
100 :
101 :
102 : bool IsWasmMemory(const void* buffer_start);
103 :
104 : bool IsWasmSharedMemory(const void* buffer_start);
105 :
106 : // Returns whether the given buffer is a Wasm memory with guard regions large
107 : // enough to safely use trap handlers.
108 : bool HasFullGuardRegions(const void* buffer_start);
109 :
110 : // Returns a pointer to a Wasm buffer's allocation data, or nullptr if the
111 : // buffer is not tracked.
112 : const AllocationData* FindAllocationData(const void* buffer_start);
113 :
114 : // Checks if a buffer points to a Wasm memory and if so does any necessary
115 : // work to reclaim the buffer. If this function returns false, the caller must
116 : // free the buffer manually.
117 : bool FreeMemoryIfIsWasmMemory(Isolate* isolate, const void* buffer_start);
118 :
119 : // When WebAssembly.Memory is transferred over PostMessage, register the
120 : // allocation as shared and track the memory objects that will need
121 : // updating if memory is resized.
122 : void RegisterWasmMemoryAsShared(Handle<WasmMemoryObject> object,
123 : Isolate* isolate);
124 :
125 : // This method is called when the underlying backing store is grown, but
126 : // instances that share the backing_store have not yet been updated.
127 : void SetPendingUpdateOnGrow(Handle<JSArrayBuffer> old_buffer,
128 : size_t new_size);
129 :
130 : // Interrupt handler for GROW_SHARED_MEMORY interrupt. Update memory objects
131 : // and instances that share the memory objects after a Grow call.
132 : void UpdateSharedMemoryInstances(Isolate* isolate);
133 :
134 : // Due to timing of when buffers are garbage collected, vs. when isolate
135 : // object handles are destroyed, it is possible to leak global handles. To
136 : // avoid this, cleanup any global handles on isolate destruction if any exist.
137 : void DeleteSharedMemoryObjectsOnIsolate(Isolate* isolate);
138 :
139 : // Allocation results are reported to UMA
140 : //
141 : // See wasm_memory_allocation_result in counters.h
142 : enum class AllocationStatus {
143 : kSuccess, // Succeeded on the first try
144 :
145 : kSuccessAfterRetry, // Succeeded after garbage collection
146 :
147 : kAddressSpaceLimitReachedFailure, // Failed because Wasm is at its address
148 : // space limit
149 :
150 : kOtherFailure // Failed for an unknown reason
151 : };
152 :
153 : private:
154 : // Helper methods to free memory only if not shared by other isolates, memory
155 : // objects.
156 : void FreeMemoryIfNotShared_Locked(Isolate* isolate,
157 : const void* backing_store);
158 : bool CanFreeSharedMemory_Locked(const void* backing_store);
159 : void RemoveSharedBufferState_Locked(Isolate* isolate,
160 : const void* backing_store);
161 :
162 : // Registers the allocation as shared, and tracks all the memory objects
163 : // associates with this allocation across isolates.
164 : void RegisterSharedWasmMemory_Locked(Handle<WasmMemoryObject> object,
165 : Isolate* isolate);
166 :
167 : // Map the new size after grow to the buffer backing store, so that instances
168 : // and memory objects that share the WebAssembly.Memory across isolates can
169 : // be updated..
170 : void AddBufferToGrowMap_Locked(Handle<JSArrayBuffer> old_buffer,
171 : size_t new_size);
172 :
173 : // Trigger a GROW_SHARED_MEMORY interrupt on all the isolates that have memory
174 : // objects that share this buffer.
175 : void TriggerSharedGrowInterruptOnAllIsolates_Locked(
176 : Handle<JSArrayBuffer> old_buffer);
177 :
178 : // When isolates hit a stack check, update the memory objects associated with
179 : // that isolate.
180 : void UpdateSharedMemoryStateOnInterrupt_Locked(Isolate* isolate,
181 : void* backing_store,
182 : size_t new_size);
183 :
184 : // Check if all the isolates that share a backing_store have hit a stack
185 : // check. If a stack check is hit, and the backing store is pending grow,
186 : // this isolate will have updated memory objects.
187 : bool AreAllIsolatesUpdated_Locked(const void* backing_store);
188 :
189 : // If a grow call is made to a buffer with a pending grow, and all the
190 : // isolates that share this buffer have not hit a StackCheck, clear the set of
191 : // already updated instances so they can be updated with the new size on the
192 : // most recent grow call.
193 : void ClearUpdatedInstancesOnPendingGrow_Locked(const void* backing_store);
194 :
195 : // Helper functions to update memory objects on grow, and maintain state for
196 : // which isolates hit a stack check.
197 : void UpdateMemoryObjectsForIsolate_Locked(Isolate* isolate,
198 : void* backing_store,
199 : size_t new_size);
200 : bool MemoryObjectsNeedUpdate_Locked(Isolate* isolate,
201 : const void* backing_store);
202 :
203 : // Destroy global handles to memory objects, and remove backing store from
204 : // isolates_per_buffer on Free.
205 : void DestroyMemoryObjectsAndRemoveIsolateEntry_Locked(
206 : Isolate* isolate, const void* backing_store);
207 : void DestroyMemoryObjectsAndRemoveIsolateEntry_Locked(
208 : const void* backing_store);
209 :
210 : void RemoveIsolateFromBackingStore_Locked(Isolate* isolate,
211 : const void* backing_store);
212 :
213 : // Removes an allocation from the tracker.
214 : AllocationData ReleaseAllocation_Locked(Isolate* isolate,
215 : const void* buffer_start);
216 :
217 : // Clients use a two-part process. First they "reserve" the address space,
218 : // which signifies an intent to actually allocate it. This determines whether
219 : // doing the allocation would put us over our limit. Once there is a
220 : // reservation, clients can do the allocation and register the result.
221 : //
222 : // We should always have:
223 : // allocated_address_space_ <= reserved_address_space_ <= kAddressSpaceLimit
224 : std::atomic<size_t> reserved_address_space_{0};
225 :
226 : // Used to protect access to the allocated address space counter and
227 : // allocation map. This is needed because Wasm memories can be freed on
228 : // another thread by the ArrayBufferTracker.
229 : base::Mutex mutex_;
230 :
231 : size_t allocated_address_space_ = 0;
232 :
233 : //////////////////////////////////////////////////////////////////////////////
234 : // Protected by {mutex_}:
235 :
236 : // Track Wasm memory allocation information. This is keyed by the start of the
237 : // buffer, rather than by the start of the allocation.
238 : std::unordered_map<const void*, AllocationData> allocations_;
239 :
240 : // Maps each buffer to the isolates that share the backing store.
241 : std::unordered_map<const void*, std::unordered_set<Isolate*>>
242 : isolates_per_buffer_;
243 :
244 : // Maps which isolates have had a grow interrupt handled on the buffer. This
245 : // is maintained to ensure that the instances are updated with the right size
246 : // on Grow.
247 : std::unordered_map<const void*, std::unordered_set<Isolate*>>
248 : isolates_updated_on_grow_;
249 :
250 : // Maps backing stores(void*) to the size of the underlying memory in
251 : // (size_t). An entry to this map is made on a grow call to the corresponding
252 : // backing store. On consecutive grow calls to the same backing store,
253 : // the size entry is updated. This entry is made right after the mprotect
254 : // call to change the protections on a backing_store, so the memory objects
255 : // have not been updated yet. The backing store entry in this map is erased
256 : // when all the memory objects, or instances that share this backing store
257 : // have their bounds updated.
258 : std::unordered_map<void*, size_t> grow_update_map_;
259 :
260 : // End of fields protected by {mutex_}.
261 : //////////////////////////////////////////////////////////////////////////////
262 :
263 : DISALLOW_COPY_AND_ASSIGN(WasmMemoryTracker);
264 : };
265 :
266 : // Attempts to allocate an array buffer with guard regions suitable for trap
267 : // handling. If address space is not available, it will return a buffer with
268 : // mini-guards that will require bounds checks.
269 : MaybeHandle<JSArrayBuffer> NewArrayBuffer(Isolate*, size_t size);
270 :
271 : // Attempts to allocate a SharedArrayBuffer with guard regions suitable for
272 : // trap handling. If address space is not available, it will try to reserve
273 : // up to the maximum for that memory. If all else fails, it will return a
274 : // buffer with mini-guards of initial size.
275 : MaybeHandle<JSArrayBuffer> NewSharedArrayBuffer(Isolate*, size_t initial_size,
276 : size_t max_size);
277 :
278 : Handle<JSArrayBuffer> SetupArrayBuffer(
279 : Isolate*, void* backing_store, size_t size, bool is_external,
280 : SharedFlag shared = SharedFlag::kNotShared);
281 :
282 : void DetachMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer,
283 : bool free_memory);
284 :
285 : } // namespace wasm
286 : } // namespace internal
287 : } // namespace v8
288 :
289 : #endif // V8_WASM_WASM_MEMORY_H_
|