v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-code-pointer-table.h
Go to the documentation of this file.
1// Copyright 2024 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_CODE_POINTER_TABLE_H_
6#define V8_WASM_WASM_CODE_POINTER_TABLE_H_
7
10
11#if !V8_ENABLE_WEBASSEMBLY
12#error This header should only be included if WebAssembly is enabled.
13#endif // !V8_ENABLE_WEBASSEMBLY
14
15namespace v8::internal::wasm {
16
17// Defines the entries in the WasmCodePointerTable and specifies the encoding.
18// When entries are in use, they contain the address of a valid Wasm code entry,
19// while free entries contain an index to the next element in the freelist.
20//
21// All reads and writes use relaxed memory ordering and need to be synchronized
22// by the caller.
24 // We write-protect the WasmCodePointerTable on platforms that support it for
25 // forward-edge CFI.
26 static constexpr bool IsWriteProtected = true;
27
28 // Set the entry to point to a given entrypoint.
29 inline void MakeCodePointerEntry(Address entrypoint, uint64_t signature_hash);
30 inline void UpdateCodePointerEntry(Address entrypoint,
31 uint64_t signature_hash);
32
33 // Make this entry a freelist entry, containing the index of the next entry
34 // on the freelist.
35 inline void MakeFreelistEntry(uint32_t next_entry_index);
36
37 // Load code entrypoint pointer stored in this entry.
38 inline Address GetEntrypoint(uint64_t signature_hash) const;
39
40 // Load code entrypoint pointer stored in this entry.
42
43 // Get the index of the next entry on the freelist.
44 inline uint32_t GetNextFreelistEntryIndex() const;
45
46 private:
48
49 std::atomic<Address> entrypoint_;
50#ifdef V8_ENABLE_SANDBOX
51 uint64_t signature_hash_;
52#endif
53};
54
55// A table for storing valid Wasm code entrypoints. This table allows enforcing
56// forward-edge CFI for Wasm calls:
57// * The table gets write-protected (e.g. with pkeys) to prevent corruption of
58// entries.
59// * At write time, we will check that the value is a valid entrypoint as
60// tracked in our CFI metadata.
61// * Wasm calls can then be replaced with a bounds-checked table lookup + call
62// to enforce that only valid entrypoints can be called.
63//
64// All methods are thread-safe if not specified otherwise.
66 : public SegmentedTable<WasmCodePointerTableEntry,
67 kCodePointerTableReservationSize> {
70
71 public:
75
76 using Handle = uint32_t;
77 static constexpr Handle kInvalidHandle = -1;
78#ifdef V8_ENABLE_SANDBOX
79 static constexpr int kOffsetOfSignatureHash =
80 offsetof(WasmCodePointerTableEntry, signature_hash_);
81#endif
82
83#ifdef V8_TARGET_ARCH_64_BIT
84 // 64-bit architectures reserve a large table upfront, hence there's a fixed
85 // maximum number of Wasm code pointers.
86 static constexpr size_t kMaxWasmCodePointers = kMaxCapacity;
87#endif
88
90
91 // The table should be initialized exactly once before use.
92 void Initialize();
93
94 // Free any resources used by the table.
95 void TearDown();
96
97 // Read the entrypoint at a given index.
98 inline Address GetEntrypoint(WasmCodePointer index,
99 uint64_t signature_hash) const;
100
101 inline Address GetEntrypointWithoutSignatureCheck(
102 WasmCodePointer index) const;
103
104 // Sets the entrypoint of the entry referenced by the given index.
105 // The Unlocked version can be used in loops, but you need to hold a
106 // `WriteScope` while calling it.
107 inline void UpdateEntrypoint(WasmCodePointer index, Address value,
108 uint64_t signature_hash);
109 inline void SetEntrypointAndSignature(WasmCodePointer index, Address value,
110 uint64_t signature_hash);
111 inline void SetEntrypointWithWriteScope(WasmCodePointer index, Address value,
112 uint64_t signature_hash,
113 WriteScope& write_scope);
114
115 // Allocates a new entry in the table and optionally initialize it.
116 inline WasmCodePointer AllocateAndInitializeEntry(Address entrypoint,
117 uint64_t signature_hash);
118 inline WasmCodePointer AllocateUninitializedEntry();
119
120 // Free an entry, which will add it to the free list.
121 inline void FreeEntry(WasmCodePointer index);
122
123 // Iterate through the freelist to find and unmap empty segments. Will return
124 // early if there's less than `threshold` many elements in the freelist.
125 void SweepSegments(size_t threshold = 2 * kEntriesPerSegment);
126
127 // Add an entry for a native function address, used by the C API.
128 WasmCodePointer GetOrCreateHandleForNativeFunction(Address addr);
129
130 // Compare the address of the entry.
131 bool EntrypointEqualTo(WasmCodePointer index, Address address);
132
133 private:
134 // Allow the ExternalReference to access the table base.
135 friend class ::v8::internal::ExternalReference;
136
137 // This marker is used to temporarily unlink the freelist to get exclusive
138 // access.
139 static constexpr FreelistHead kRetryMarker = FreelistHead(0xffffffff, 0);
140 static bool IsRetryMarker(FreelistHead freelist) {
141 return freelist.length() == kRetryMarker.length() &&
142 freelist.next() == kRetryMarker.next();
143 }
144
145 // Access the Freelist head, retrying if the retry marker is seen.
146 V8_INLINE FreelistHead ReadFreelistHead();
147
148 // Allocate an entry either from the freelist or creating a new segment.
150
151 // Atomically link a freelist into the current freelist head.
152 V8_INLINE FreelistHead LinkFreelist(FreelistHead new_freelist,
153 uint32_t last_element);
154
155 // Helper functions for converting a freelist to a vector and back.
156 // Freelist access is not atomic.
157 std::vector<uint32_t> FreelistToVector(FreelistHead freelist);
158 FreelistHead VectorToFreelist(std::vector<uint32_t> entries);
159
160 // Try to allocate the first entry of the freelist.
161 //
162 // This method is mostly a wrapper around an atomic compare-and-swap which
163 // replaces the current freelist head with the next entry in the freelist,
164 // thereby allocating the entry at the start of the freelist.
165 V8_INLINE bool TryAllocateFromFreelist(uint32_t* index);
166
167 // Not atomic and should only be used if you have exclusive access to the
168 // freelist.
169 V8_INLINE uint32_t
170 AllocateEntryFromFreelistNonAtomic(FreelistHead* freelist_head);
171
172 // Free all handles in the `native_function_map_`.
173 void FreeNativeFunctionHandles();
174
175 std::atomic<FreelistHead> freelist_head_ = FreelistHead();
176 // The mutex is used to avoid two threads from concurrently allocating
177 // segments and using more memory than needed.
179
181 std::map<Address, WasmCodePointer> native_function_map_;
182
183 friend class WasmCodePointerTableTest;
184};
185
187
188} // namespace v8::internal::wasm
189
190#endif // V8_WASM_WASM_CODE_POINTER_TABLE_H_
WasmCodePointerTable(const WasmCodePointerTable &)=delete
static bool IsRetryMarker(FreelistHead freelist)
WasmCodePointerTable & operator=(const WasmCodePointerTable &)=delete
std::vector< uint32_t > FreelistToVector(FreelistHead freelist)
std::map< Address, WasmCodePointer > native_function_map_
ZoneVector< Entry > entries
V8_EXPORT_PRIVATE WasmCodePointerTable * GetProcessWideWasmCodePointerTable()
constexpr size_t kCodePointerTableReservationSize
#define V8_EXPORT_PRIVATE
Definition macros.h:460
void UpdateCodePointerEntry(Address entrypoint, uint64_t signature_hash)
void MakeCodePointerEntry(Address entrypoint, uint64_t signature_hash)
Address GetEntrypoint(uint64_t signature_hash) const
#define V8_INLINE
Definition v8config.h:500