v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
trusted-pointer-table-inl.h
Go to the documentation of this file.
1// Copyright 2023 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_SANDBOX_TRUSTED_POINTER_TABLE_INL_H_
6#define V8_SANDBOX_TRUSTED_POINTER_TABLE_INL_H_
7
9// Include the non-inl header before the rest of the headers.
10
12#include "src/sandbox/sandbox.h"
14
15#ifdef V8_ENABLE_SANDBOX
16
17namespace v8 {
18namespace internal {
19
20void TrustedPointerTableEntry::MakeTrustedPointerEntry(Address pointer,
22 bool mark_as_alive) {
23 auto payload = Payload::ForTrustedPointerEntry(pointer, tag);
24 if (mark_as_alive) payload.SetMarkBit();
25 payload_.store(payload, std::memory_order_relaxed);
26}
27
28void TrustedPointerTableEntry::MakeFreelistEntry(uint32_t next_entry_index) {
29 auto payload = Payload::ForFreelistEntry(next_entry_index);
30 payload_.store(payload, std::memory_order_relaxed);
31}
32
33void TrustedPointerTableEntry::MakeZappedEntry() {
34 auto payload = Payload::ForZappedEntry();
35 payload_.store(payload, std::memory_order_relaxed);
36}
37
38Address TrustedPointerTableEntry::GetPointer(IndirectPointerTag tag) const {
39 DCHECK(!IsFreelistEntry());
40 return payload_.load(std::memory_order_relaxed).Untag(tag);
41}
42
43void TrustedPointerTableEntry::SetPointer(Address pointer,
45 DCHECK(!IsFreelistEntry());
46 // Currently, this method is only used when the mark bit is unset. If this
47 // ever changes, we'd need to check the marking state of the old entry and
48 // set the marking state of the new entry accordingly.
49 DCHECK(!payload_.load(std::memory_order_relaxed).HasMarkBitSet());
50 auto new_payload = Payload::ForTrustedPointerEntry(pointer, tag);
51 DCHECK(!new_payload.HasMarkBitSet());
52 payload_.store(new_payload, std::memory_order_relaxed);
53}
54
55bool TrustedPointerTableEntry::HasPointer(IndirectPointerTag tag) const {
56 auto payload = payload_.load(std::memory_order_relaxed);
57 if (!payload.ContainsPointer()) return false;
58 return tag == kUnknownIndirectPointerTag || payload.IsTaggedWith(tag);
59}
60
61void TrustedPointerTableEntry::OverwriteTag(IndirectPointerTag tag) {
62 // Changing tags could defeat security defenses, so guard this method
63 // against misuse by allow-listing specific operations.
64 CHECK_EQ(tag, kUnpublishedIndirectPointerTag);
65
66 auto old_payload = payload_.load(std::memory_order_relaxed);
67 auto new_payload = old_payload;
68 new_payload.SetTag(tag);
69 // Unpublishing entries is monotonic, so we don't need to loop here.
70 bool success = payload_.compare_exchange_strong(old_payload, new_payload,
71 std::memory_order_relaxed);
72 DCHECK(success || old_payload.IsTaggedWith(kUnpublishedIndirectPointerTag));
73 USE(success);
74}
75
76bool TrustedPointerTableEntry::IsFreelistEntry() const {
77 auto payload = payload_.load(std::memory_order_relaxed);
78 return payload.ContainsFreelistLink();
79}
80
81uint32_t TrustedPointerTableEntry::GetNextFreelistEntryIndex() const {
82 return payload_.load(std::memory_order_relaxed).ExtractFreelistLink();
83}
84
85void TrustedPointerTableEntry::Mark() {
86 auto old_payload = payload_.load(std::memory_order_relaxed);
87 DCHECK(old_payload.ContainsPointer());
88
89 auto new_payload = old_payload;
90 new_payload.SetMarkBit();
91
92 // We don't need to perform the CAS in a loop since it can only fail if a new
93 // value has been written into the entry. This, however, will also have set
94 // the marking bit.
95 bool success = payload_.compare_exchange_strong(old_payload, new_payload,
96 std::memory_order_relaxed);
97 DCHECK(success || old_payload.HasMarkBitSet());
98 USE(success);
99}
100
101void TrustedPointerTableEntry::Unmark() {
102 auto payload = payload_.load(std::memory_order_relaxed);
103 payload.ClearMarkBit();
104 payload_.store(payload, std::memory_order_relaxed);
105}
106
107bool TrustedPointerTableEntry::IsMarked() const {
108 return payload_.load(std::memory_order_relaxed).HasMarkBitSet();
109}
110
111bool TrustedPointerTable::IsUnpublished(TrustedPointerHandle handle) const {
112 uint32_t index = HandleToIndex(handle);
113 return at(index).HasPointer(kUnpublishedIndirectPointerTag);
114}
115
116Address TrustedPointerTable::Get(TrustedPointerHandle handle,
117 IndirectPointerTag tag) const {
118 uint32_t index = HandleToIndex(handle);
119#if defined(V8_USE_ADDRESS_SANITIZER)
120 // We rely on the tagging scheme to produce non-canonical addresses when an
121 // entry isn't tagged with the expected tag. Such "safe" crashes can then be
122 // filtered out by our sandbox crash filter. However, when ASan is active, it
123 // may perform its shadow memory access prior to the actual memory access.
124 // For a non-canonical address, this can lead to a segfault at a _canonical_
125 // address, which our crash filter can then not distinguish from a "real"
126 // crash. Therefore, in ASan builds, we perform an additional CHECK here that
127 // the entry is tagged with the expected tag. The resulting CHECK failure
128 // will then be ignored by the crash filter.
129 // This check is, however, not needed when accessing the null entry, as that
130 // is always valid (it just contains nullptr).
131 CHECK(index == 0 || at(index).HasPointer(tag));
132#else
133 // Otherwise, this is just a DCHECK.
134 DCHECK(index == 0 || at(index).HasPointer(tag));
135#endif
136 return at(index).GetPointer(tag);
137}
138
139Address TrustedPointerTable::GetMaybeUnpublished(TrustedPointerHandle handle,
140 IndirectPointerTag tag) const {
141 uint32_t index = HandleToIndex(handle);
142 const TrustedPointerTableEntry& entry = at(index);
143 if (entry.HasPointer(kUnpublishedIndirectPointerTag)) {
144 return entry.GetPointer(kUnpublishedIndirectPointerTag);
145 }
146#if defined(V8_USE_ADDRESS_SANITIZER)
147 // See comment above.
148 CHECK(index == 0 || entry.HasPointer(tag));
149#else
150 DCHECK(index == 0 || entry.HasPointer(tag));
151#endif
152 return entry.GetPointer(tag);
153}
154
155void TrustedPointerTable::Set(TrustedPointerHandle handle, Address pointer,
156 IndirectPointerTag tag) {
158 Validate(pointer, tag);
159 uint32_t index = HandleToIndex(handle);
160 at(index).SetPointer(pointer, tag);
161}
162
163TrustedPointerHandle TrustedPointerTable::AllocateAndInitializeEntry(
164 Space* space, Address pointer, IndirectPointerTag tag,
165 TrustedPointerPublishingScope* scope) {
166 DCHECK(space->BelongsTo(this));
167 Validate(pointer, tag);
168 uint32_t index = AllocateEntry(space);
169 at(index).MakeTrustedPointerEntry(pointer, tag, space->allocate_black());
170 if (scope != nullptr) scope->TrackPointer(&at(index));
171 return IndexToHandle(index);
172}
173
174void TrustedPointerTable::Mark(Space* space, TrustedPointerHandle handle) {
175 DCHECK(space->BelongsTo(this));
176 // The null entry is immortal and immutable, so no need to mark it as alive.
177 if (handle == kNullTrustedPointerHandle) return;
178
179 uint32_t index = HandleToIndex(handle);
180 DCHECK(space->Contains(index));
181
182 at(index).Mark();
183}
184
185void TrustedPointerTable::Zap(TrustedPointerHandle handle) {
186 uint32_t index = HandleToIndex(handle);
187 at(index).MakeZappedEntry();
188}
189
190template <typename Callback>
191void TrustedPointerTable::IterateActiveEntriesIn(Space* space,
192 Callback callback) {
193 IterateEntriesIn(space, [&](uint32_t index) {
194 if (!at(index).IsFreelistEntry()) {
195 Address pointer = at(index).GetPointer(kUnknownIndirectPointerTag);
196 callback(IndexToHandle(index), pointer);
197 }
198 });
199}
200
201uint32_t TrustedPointerTable::HandleToIndex(TrustedPointerHandle handle) const {
202 uint32_t index = handle >> kTrustedPointerHandleShift;
204 return index;
205}
206
207TrustedPointerHandle TrustedPointerTable::IndexToHandle(uint32_t index) const {
210 return handle;
211}
212
213void TrustedPointerTable::Validate(Address pointer, IndirectPointerTag tag) {
215 // This CHECK is mostly just here to force tags to be taken out of the
216 // IsTrustedSpaceMigrationInProgressForObjectsWithTag function once the
217 // objects are fully migrated into trusted space.
218 DCHECK(Sandbox::current()->Contains(pointer));
219 return;
220 }
221
222 // Entries must never point into the sandbox, as they couldn't be trusted in
223 // that case. This CHECK is a defense-in-depth mechanism to guarantee this.
224 CHECK(!InsideSandbox(pointer));
225}
226
227} // namespace internal
228} // namespace v8
229
230#endif // V8_ENABLE_SANDBOX
231
232#endif // V8_SANDBOX_TRUSTED_POINTER_TABLE_INL_H_
TNode< Object > callback
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
IndirectPointerHandle TrustedPointerHandle
constexpr TrustedPointerHandle kNullTrustedPointerHandle
constexpr uint32_t kTrustedPointerHandleShift
V8_INLINE constexpr bool IsTrustedSpaceMigrationInProgressForObjectsWithTag(IndirectPointerTag tag)
V8_INLINE bool InsideSandbox(uintptr_t address)
Definition sandbox.h:334
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293