• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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