Loading...
Searching...
No Matches
v8-sandbox.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 INCLUDE_V8_SANDBOX_H_
6#define INCLUDE_V8_SANDBOX_H_
7
8#include <cstdint>
9
10#include "v8-internal.h" // NOLINT(build/include_directory)
11#include "v8config.h" // NOLINT(build/include_directory)
12
13namespace v8 {
14
28enum class CppHeapPointerTag : uint16_t {
29 kFirstTag = 0,
30 kNullTag = 0,
31
56 kDefaultTag = 0x7000,
57
58 kZappedEntryTag = 0x7ffd,
59 kEvacuationEntryTag = 0x7ffe,
60 kFreeEntryTag = 0x7fff,
61 // The tags are limited to 15 bits, so the last tag is 0x7fff.
62 kLastTag = 0x7fff,
63};
64
65// Convenience struct to represent tag ranges. This is used for type checks
66// against supertypes, which cover a range of types (their subtypes).
67// Both the lower- and the upper bound are inclusive. In other words, this
68// struct represents the range [lower_bound, upper_bound].
69// TODO(saelo): reuse internal::TagRange here.
73 : lower_bound(lower), upper_bound(upper) {}
76
77 // Check whether the tag of the given CppHeapPointerTable entry is within
78 // this range. This method encodes implementation details of the
79 // CppHeapPointerTable, which is necessary as it is used by
80 // ReadCppHeapPointerField below.
81 // Returns true if the check is successful and the tag of the given entry is
82 // within this range, false otherwise.
83 bool CheckTagOf(uint64_t entry) {
84 // Note: the cast to uint32_t is important here. Otherwise, the uint16_t's
85 // would be promoted to int in the range check below, which would result in
86 // undefined behavior (signed integer undeflow) if the actual value is less
87 // than the lower bound. Then, the compiler would take advantage of the
88 // undefined behavior and turn the range check into a simple
89 // `actual_tag <= last_tag` comparison, which is incorrect.
90 uint32_t actual_tag = static_cast<uint16_t>(entry);
91 // The actual_tag is shifted to the left by one and contains the marking
92 // bit in the LSB. To ignore that during the type check, simply add one to
93 // the (shifted) range.
94 constexpr int kTagShift = internal::kCppHeapPointerTagShift;
95 uint32_t first_tag = static_cast<uint32_t>(lower_bound) << kTagShift;
96 uint32_t last_tag = (static_cast<uint32_t>(upper_bound) << kTagShift) + 1;
97 return actual_tag >= first_tag && actual_tag <= last_tag;
98 }
99};
100
103
111 public:
118
128};
129
130namespace internal {
131
132#ifdef V8_COMPRESS_POINTERS
133V8_INLINE static Address* GetCppHeapPointerTableBase(v8::Isolate* isolate) {
134 Address addr = reinterpret_cast<Address>(isolate) +
135 Internals::kIsolateCppHeapPointerTableOffset +
137 return *reinterpret_cast<Address**>(addr);
138}
139#endif // V8_COMPRESS_POINTERS
140
141template <typename T>
142V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate,
143 Address heap_object_ptr, int offset,
144 CppHeapPointerTagRange tag_range) {
145#ifdef V8_COMPRESS_POINTERS
146 // See src/sandbox/cppheap-pointer-table-inl.h. Logic duplicated here so
147 // it can be inlined and doesn't require an additional call.
148 const CppHeapPointerHandle handle =
149 Internals::ReadRawField<CppHeapPointerHandle>(heap_object_ptr, offset);
150 const uint32_t index = handle >> kExternalPointerIndexShift;
151 const Address* table = GetCppHeapPointerTableBase(isolate);
152 const std::atomic<Address>* ptr =
153 reinterpret_cast<const std::atomic<Address>*>(&table[index]);
154 Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed);
155
156 Address pointer = entry;
157 if (V8_LIKELY(tag_range.CheckTagOf(entry))) {
158 pointer = entry >> kCppHeapPointerPayloadShift;
159 } else {
160 // If the type check failed, we simply return nullptr here. That way:
161 // 1. The null handle always results in nullptr being returned here, which
162 // is a desired property. Otherwise, we would need an explicit check for
163 // the null handle above, and therefore an additional branch. This
164 // works because the 0th entry of the table always contains nullptr
165 // tagged with the null tag (i.e. an all-zeros entry). As such,
166 // regardless of whether the type check succeeds, the result will
167 // always be nullptr.
168 // 2. The returned pointer is guaranteed to crash even on platforms with
169 // top byte ignore (TBI), such as Arm64. The alternative would be to
170 // simply return the original entry with the left-shifted payload.
171 // However, due to TBI, an access to that may not always result in a
172 // crash (specifically, if the second most significant byte happens to
173 // be zero). In addition, there shouldn't be a difference on Arm64
174 // between returning nullptr or the original entry, since it will
175 // simply compile to a `csel x0, x8, xzr, lo` instead of a
176 // `csel x0, x10, x8, lo` instruction.
177 pointer = 0;
178 }
179 return reinterpret_cast<T*>(pointer);
180#else // !V8_COMPRESS_POINTERS
181 return reinterpret_cast<T*>(
182 Internals::ReadRawField<Address>(heap_object_ptr, offset));
183#endif // !V8_COMPRESS_POINTERS
184}
185
186} // namespace internal
187} // namespace v8
188
189#endif // INCLUDE_V8_SANDBOX_H_
Definition: v8-isolate.h:290
Definition: v8-sandbox.h:110
static void InitializeBeforeThreadCreation()
static void PrepareCurrentThreadForHardwareSandboxing()
static const int kExternalPointerTableBasePointerOffset
Definition: v8-internal.h:927
uint32_t CppHeapPointerHandle
Definition: v8-internal.h:383
constexpr uint64_t kCppHeapPointerPayloadShift
Definition: v8-internal.h:400
constexpr uint64_t kCppHeapPointerTagShift
Definition: v8-internal.h:399
uintptr_t Address
Definition: v8-internal.h:52
Definition: libplatform.h:15
CppHeapPointerTag
Definition: v8-sandbox.h:28
constexpr CppHeapPointerTagRange kAnyCppHeapPointer(CppHeapPointerTag::kFirstTag, CppHeapPointerTag::kLastTag)
Definition: v8-sandbox.h:70
constexpr CppHeapPointerTagRange(CppHeapPointerTag lower, CppHeapPointerTag upper)
Definition: v8-sandbox.h:71
bool CheckTagOf(uint64_t entry)
Definition: v8-sandbox.h:83
CppHeapPointerTag lower_bound
Definition: v8-sandbox.h:74
CppHeapPointerTag upper_bound
Definition: v8-sandbox.h:75
#define V8_EXPORT
Definition: v8config.h:860
#define V8_INLINE
Definition: v8config.h:513
#define V8_LIKELY(condition)
Definition: v8config.h:674