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 :
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:
27 178857 : WasmMemoryTracker() = default;
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 : // Use {kSoftLimit} if you can implement a fallback which needs less reserved
34 : // memory.
35 : enum ReservationLimit { kSoftLimit, kHardLimit };
36 : bool ReserveAddressSpace(size_t num_bytes, ReservationLimit limit);
37 :
38 : void RegisterAllocation(Isolate* isolate, void* allocation_base,
39 : size_t allocation_length, void* buffer_start,
40 : size_t buffer_length);
41 :
42 : struct AllocationData {
43 : void* allocation_base = nullptr;
44 : size_t allocation_length = 0;
45 : void* buffer_start = nullptr;
46 : size_t buffer_length = 0;
47 :
48 : private:
49 : AllocationData() = default;
50 : AllocationData(void* allocation_base, size_t allocation_length,
51 : void* buffer_start, size_t buffer_length)
52 : : allocation_base(allocation_base),
53 : allocation_length(allocation_length),
54 : buffer_start(buffer_start),
55 176529 : buffer_length(buffer_length) {
56 : DCHECK_LE(reinterpret_cast<uintptr_t>(allocation_base),
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));
61 : DCHECK_GE(
62 : reinterpret_cast<uintptr_t>(allocation_base) + allocation_length,
63 : reinterpret_cast<uintptr_t>(buffer_start) + buffer_length);
64 : }
65 :
66 : friend WasmMemoryTracker;
67 : };
68 :
69 : // Allow tests to allocate a backing store the same way as we do it for
70 : // WebAssembly memory. This is used in unit tests for trap handler to
71 : // generate the same signals/exceptions for invalid memory accesses as
72 : // we would get with WebAssembly memory.
73 : V8_EXPORT_PRIVATE void* TryAllocateBackingStoreForTesting(
74 : Heap* heap, size_t size, void** allocation_base,
75 : size_t* allocation_length);
76 :
77 : // Free memory allocated with TryAllocateBackingStoreForTesting.
78 : V8_EXPORT_PRIVATE void FreeBackingStoreForTesting(base::AddressRegion memory,
79 : void* buffer_start);
80 :
81 : // Decreases the amount of reserved address space.
82 : void ReleaseReservation(size_t num_bytes);
83 :
84 : // Removes an allocation from the tracker.
85 : AllocationData ReleaseAllocation(Isolate* isolate, const void* buffer_start);
86 :
87 : bool IsWasmMemory(const void* buffer_start);
88 :
89 : // Returns whether the given buffer is a Wasm memory with guard regions large
90 : // enough to safely use trap handlers.
91 : bool HasFullGuardRegions(const void* buffer_start);
92 :
93 : // Returns a pointer to a Wasm buffer's allocation data, or nullptr if the
94 : // buffer is not tracked.
95 : const AllocationData* FindAllocationData(const void* buffer_start);
96 :
97 : // Checks if a buffer points to a Wasm memory and if so does any necessary
98 : // work to reclaim the buffer. If this function returns false, the caller must
99 : // free the buffer manually.
100 : bool FreeMemoryIfIsWasmMemory(Isolate* isolate, const void* buffer_start);
101 :
102 : // Allocation results are reported to UMA
103 : //
104 : // See wasm_memory_allocation_result in counters.h
105 : enum class AllocationStatus {
106 : kSuccess, // Succeeded on the first try
107 :
108 : kSuccessAfterRetry, // Succeeded after garbage collection
109 :
110 : kAddressSpaceLimitReachedFailure, // Failed because Wasm is at its address
111 : // space limit
112 :
113 : kOtherFailure // Failed for an unknown reason
114 : };
115 :
116 : private:
117 : void AddAddressSpaceSample(Isolate* isolate);
118 :
119 : // Clients use a two-part process. First they "reserve" the address space,
120 : // which signifies an intent to actually allocate it. This determines whether
121 : // doing the allocation would put us over our limit. Once there is a
122 : // reservation, clients can do the allocation and register the result.
123 : //
124 : // We should always have:
125 : // allocated_address_space_ <= reserved_address_space_ <= kAddressSpaceLimit
126 : std::atomic<size_t> reserved_address_space_{0};
127 :
128 : // Used to protect access to the allocated address space counter and
129 : // allocation map. This is needed because Wasm memories can be freed on
130 : // another thread by the ArrayBufferTracker.
131 : base::Mutex mutex_;
132 :
133 : size_t allocated_address_space_ = 0;
134 :
135 : // Track Wasm memory allocation information. This is keyed by the start of the
136 : // buffer, rather than by the start of the allocation.
137 : std::unordered_map<const void*, AllocationData> allocations_;
138 :
139 : DISALLOW_COPY_AND_ASSIGN(WasmMemoryTracker);
140 : };
141 :
142 : // Attempts to allocate an array buffer with guard regions suitable for trap
143 : // handling. If address space is not available, it will return a buffer with
144 : // mini-guards that will require bounds checks.
145 : MaybeHandle<JSArrayBuffer> NewArrayBuffer(
146 : Isolate*, size_t size, SharedFlag shared = SharedFlag::kNotShared);
147 :
148 : Handle<JSArrayBuffer> SetupArrayBuffer(
149 : Isolate*, void* backing_store, size_t size, bool is_external,
150 : SharedFlag shared = SharedFlag::kNotShared);
151 :
152 : void DetachMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer,
153 : bool free_memory);
154 :
155 : } // namespace wasm
156 : } // namespace internal
157 : } // namespace v8
158 :
159 : #endif // V8_WASM_WASM_MEMORY_H_
|