v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
indirect-pointer-tag.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_INDIRECT_POINTER_TAG_H_
6#define V8_SANDBOX_INDIRECT_POINTER_TAG_H_
7
10
11namespace v8 {
12namespace internal {
13
14// Defines the list of valid indirect pointer tags.
15//
16// When accessing a trusted/indirect pointer, an IndirectPointerTag must be
17// provided which indicates the expected instance type of the pointed-to
18// object. When the sandbox is enabled, this tag is used to ensure type-safe
19// access to objects referenced via trusted pointers: if the provided tag
20// doesn't match the tag of the object in the trusted pointer table, an
21// inaccessible pointer will be returned.
22//
23// We use the shifted instance type as tag and an AND-based type-checking
24// mechanism in the TrustedPointerTable, similar to the one used by the
25// ExternalPointerTable: the entry in the table is ORed with the tag and then
26// ANDed with the inverse of the tag upon access. This has the benefit that the
27// type check and the removal of the marking bit can be folded into a single
28// bitwise operations. However, it is not technically guaranteed that simply
29// using the instance type as tag works for this scheme as the bits of one
30// instance type may happen to be a superset of those of another instance type,
31// thereby causing the type check to incorrectly pass. As such, the chance of
32// getting "incompabitle" tags increases when adding more tags here so we may
33// at some point want to consider manually assigning tag values that are
34// guaranteed to work (similar for how we do it for ExternalPointerTags).
35
36constexpr int kIndirectPointerTagShift = 48;
37constexpr uint64_t kIndirectPointerTagMask = 0x7fff'0000'0000'0000;
38constexpr uint64_t kTrustedPointerTableMarkBit = 0x8000'0000'0000'0000;
39// We use a reserved bit for the free entry tag so that the
40// kUnknownIndirectPointerTag cannot untag free entries. Due to that, not all
41// tags in the kAllTagsForAndBasedTypeChecking are usable here (which is
42// ensured by static asserts below, see VALIDATE_INDIRECT_POINTER_TAG).
43// However, in practice this would probably be fine since the payload is a
44// table index, and so would likely always crash when treated as a pointer. As
45// such, if there is ever need for more tags, this can be reconsidered.
46// Note that we use a bit in the 2nd most significant byte here due to top byte
47// ignore (TBI), which allows dereferencing pointers even if bits in the most
48// significant byte are set.
49constexpr uint64_t kTrustedPointerTableFreeEntryBit = 0x0080'0000'0000'0000;
51 0x7f7f'0000'0000'0000;
52
53// TODO(saelo): Also switch the trusted pointer table to use a range-based type
54// checking mechanism instead of the AND-based one. This will allow us to
55// support type hierarchies and allow for more tags. See the
56// ExternalPointerTable for the type checking scheme that we should use here.
57constexpr uint64_t kAllTagsForAndBasedTypeChecking[] = {
58 0b00001111, 0b00010111, 0b00011011, 0b00011101, 0b00011110, 0b00100111,
59 0b00101011, 0b00101101, 0b00101110, 0b00110011, 0b00110101, 0b00110110,
60 0b00111001, 0b00111010, 0b00111100, 0b01000111, 0b01001011, 0b01001101,
61 0b01001110, 0b01010011, 0b01010101, 0b01010110, 0b01011001, 0b01011010,
62 0b01011100, 0b01100011, 0b01100101, 0b01100110, 0b01101001, 0b01101010,
63 0b01101100, 0b01110001, 0b01110010, 0b01110100, 0b01111000, 0b10000111,
64 0b10001011, 0b10001101, 0b10001110, 0b10010011, 0b10010101, 0b10010110,
65 0b10011001, 0b10011010, 0b10011100, 0b10100011, 0b10100101, 0b10100110,
66 0b10101001, 0b10101010, 0b10101100, 0b10110001, 0b10110010, 0b10110100,
67 0b10111000, 0b11000011, 0b11000101, 0b11000110, 0b11001001, 0b11001010,
68 0b11001100, 0b11010001, 0b11010010, 0b11010100, 0b11011000, 0b11100001,
69 0b11100010, 0b11100100, 0b11101000, 0b11110000};
70
71// Shared trusted pointers are owned by the shared Isolate and stored in the
72// shared trusted pointer table associated with that Isolate, where they can
73// be accessed from multiple threads at the same time. The objects referenced
74// in this way must therefore always be thread-safe.
75// TODO(358918874): Consider having explicitly shared types (e.g.
76// `ExposedSharedTrustedObject`) and enforcing that shared tags are only ever
77// used with shared types.
78#define SHARED_TRUSTED_POINTER_TAG_LIST(V) \
79 V(kFirstSharedTrustedTag, 1) \
80 V(kLastSharedTrustedTag, 1)
81// Leave some space in the tag range here for future shared tags.
82
83// Trusted pointers using these tags are kept in a per-Isolate trusted
84// pointer table and can only be accessed when this Isolate is active.
85#define PER_ISOLATE_INDIRECT_POINTER_TAG_LIST(V) \
86 V(kFirstPerIsolateTrustedTag, 6) \
87 V(kCodeIndirectPointerTag, 6) \
88 V(kBytecodeArrayIndirectPointerTag, 7) \
89 V(kInterpreterDataIndirectPointerTag, 8) \
90 V(kUncompiledDataIndirectPointerTag, 9) \
91 V(kRegExpDataIndirectPointerTag, 10) \
92 IF_WASM(V, kWasmTrustedInstanceDataIndirectPointerTag, 11) \
93 IF_WASM(V, kWasmInternalFunctionIndirectPointerTag, 12) \
94 IF_WASM(V, kWasmFunctionDataIndirectPointerTag, 13) \
95 IF_WASM(V, kWasmDispatchTableIndirectPointerTag, 14) \
96 V(kLastPerIsolateTrustedTag, 14)
97
98#define INDIRECT_POINTER_TAG_LIST(V) \
99 SHARED_TRUSTED_POINTER_TAG_LIST(V) \
100 PER_ISOLATE_INDIRECT_POINTER_TAG_LIST(V) \
101 V(kUnpublishedIndirectPointerTag, 34)
102
103#define MAKE_TAG(i) \
104 (kAllTagsForAndBasedTypeChecking[i] << kIndirectPointerTagShift)
105
106// TODO(saelo): consider renaming this to something like TypeTag or
107// InstanceTypeTag since that better captures what this represents.
108enum IndirectPointerTag : uint64_t {
109 // The null tag. Usually used to express the lack of a valid tag, for example
110 // in non-sandbox builds.
112
113 // This tag can be used when an indirect pointer field can legitimately refer
114 // to objects of different types.
115 // NOTE: this tag effectively disables the built-in type-checking mechanism.
116 // As such, in virtually all cases the caller needs to perform runtime-type
117 // checks (i.e. IsXyzObject(obj))` afterwards which need to be able to
118 // correctly handle unexpected types. The last point is worth stressing
119 // further. As an example, the following code is NOT correct:
120 //
121 // auto obj = LoadTrustedPointerField<kUnknownIndirectPointerTag>(...);
122 // if (IsFoo(obj)) {
123 // Cast<Foo>(obj)->foo();
124 // } else if (IsBar(obj)) {
125 // Cast<Bar>(obj)->bar();
126 // } else {
127 // // Potential type confusion here!
128 // Cast<Baz>(obj)->baz();
129 // }
130 //
131 // This is because an attacker can swap trusted pointers and thereby cause an
132 // object of a different/unexpected type to be returned. Instead, in this
133 // case a CHECK can for example be used to make the code correct:
134 //
135 // // ...
136 // } else {
137 // // Must be a Baz object
138 // CHECK(IsBaz(obj));
139 // Cast<Baz>(obj)->baz();
140 // }
141 //
143
144 // Tag used internally by the trusted pointer table to mark free entries.
145 // See also the comment above kTrustedPointerTableFreeEntryBit for why this
146 // uses a dedicated bit.
148
149// "Regular" tags. One per supported instance type.
150#define INDIRECT_POINTER_TAG_ENUM_DECL(name, tag_id) name = MAKE_TAG(tag_id),
152#undef INDIRECT_POINTER_TAG_ENUM_DECL
153};
154
155#define VALIDATE_INDIRECT_POINTER_TAG(name, tag_id) \
156 static_assert((name & kIndirectPointerTagMask) == name); \
157 static_assert((name & kIndirectPointerTagMaskWithoutFreeEntryBit) == name);
159#undef VALIDATE_INDIRECT_POINTER_TAG
164
165// True if the external pointer must be accessed from the shared isolate's
166// external pointer table.
168 IndirectPointerTag tag) {
169 static_assert(IndirectPointerTag::kFirstSharedTrustedTag <=
170 IndirectPointerTag::kLastSharedTrustedTag);
171 return tag >= IndirectPointerTag::kFirstSharedTrustedTag &&
172 tag <= IndirectPointerTag::kLastSharedTrustedTag;
173}
174
176 IndirectPointerTag tag) {
177 static_assert(IndirectPointerTag::kFirstPerIsolateTrustedTag <=
178 IndirectPointerTag::kLastPerIsolateTrustedTag);
179 return tag >= IndirectPointerTag::kFirstPerIsolateTrustedTag &&
180 tag <= IndirectPointerTag::kLastPerIsolateTrustedTag;
181}
182
186
187// Migrating objects into trusted space is typically performed in multiple
188// steps, where all references to the object from inside the sandbox are first
189// changed to indirect pointers before actually moving the object out of the
190// sandbox. As we have CHECKs that trusted pointer table entries point outside
191// of the sandbox, we need this helper function to disable that CHECK for
192// objects that are in the process of being migrated into trusted space.
197
198// The null tag is also considered an invalid tag since no indirect pointer
199// field should be using this tag.
201
204 switch (instance_type) {
205 case CODE_TYPE:
206 return kCodeIndirectPointerTag;
207 case BYTECODE_ARRAY_TYPE:
208 return kBytecodeArrayIndirectPointerTag;
209 case INTERPRETER_DATA_TYPE:
210 return kInterpreterDataIndirectPointerTag;
211 case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE:
212 case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE:
213 case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_WITH_JOB_TYPE:
214 case UNCOMPILED_DATA_WITH_PREPARSE_DATA_AND_JOB_TYPE:
215 // TODO(saelo): Consider adding support for inheritance hierarchies in
216 // our tag checking mechanism.
217 return kUncompiledDataIndirectPointerTag;
218 case ATOM_REG_EXP_DATA_TYPE:
219 case IR_REG_EXP_DATA_TYPE:
220 // TODO(saelo): Consider adding support for inheritance hierarchies in
221 // our tag checking mechanism.
222 return kRegExpDataIndirectPointerTag;
223#if V8_ENABLE_WEBASSEMBLY
224 case WASM_DISPATCH_TABLE_TYPE:
225 return kWasmDispatchTableIndirectPointerTag;
226 case WASM_TRUSTED_INSTANCE_DATA_TYPE:
227 return kWasmTrustedInstanceDataIndirectPointerTag;
228 case WASM_INTERNAL_FUNCTION_TYPE:
229 return kWasmInternalFunctionIndirectPointerTag;
230 case WASM_FUNCTION_DATA_TYPE:
231 case WASM_EXPORTED_FUNCTION_DATA_TYPE:
232 case WASM_JS_FUNCTION_DATA_TYPE:
233 case WASM_CAPI_FUNCTION_DATA_TYPE:
234 // TODO(saelo): Consider adding support for inheritance hierarchies in
235 // our tag checking mechanism.
236 return kWasmFunctionDataIndirectPointerTag;
237#endif // V8_ENABLE_WEBASSEMBLY
238 default:
239 UNREACHABLE();
240 }
241}
242
246 switch (tag) {
247#define CASE(name, instance_type, tag_id) \
248 case MAKE_TAG(tag_id): \
249 return instance_type; \
250 break;
251#undef CASE
252 default:
253 UNREACHABLE();
254 }
255}
256
257#undef MAKE_TAG
258
259// Sanity checks.
260#define CHECK_SHARED_TRUSTED_POINTER_TAGS(Tag, ...) \
261 static_assert(IsSharedTrustedPointerType(Tag));
262#define CHECK_NON_SHARED_TRUSTED_POINTER_TAGS(Tag, ...) \
263 static_assert(!IsSharedTrustedPointerType(Tag));
264
267
268#undef CHECK_NON_SHARED_TRUSTED_POINTER_TAGS
269#undef CHECK_SHARED_TRUSTED_POINTER_TAGS
270
271#undef SHARED_TRUSTED_POINTER_TAG_LIST
272#undef PER_ISOLATE_INDIRECT_POINTER_TAG_LIST
273#undef INDIRECT_POINTER_TAG_LIST
274
275} // namespace internal
276} // namespace v8
277
278#endif // V8_SANDBOX_INDIRECT_POINTER_TAG_H_
#define VALIDATE_INDIRECT_POINTER_TAG(name, tag_id)
#define CHECK_NON_SHARED_TRUSTED_POINTER_TAGS(Tag,...)
#define PER_ISOLATE_INDIRECT_POINTER_TAG_LIST(V)
#define INDIRECT_POINTER_TAG_LIST(V)
#define CHECK_SHARED_TRUSTED_POINTER_TAGS(Tag,...)
#define INDIRECT_POINTER_TAG_ENUM_DECL(name, tag_id)
#define SHARED_TRUSTED_POINTER_TAG_LIST(V)
constexpr uint64_t kIndirectPointerTagMaskWithoutFreeEntryBit
V8_INLINE InstanceType InstanceTypeFromIndirectPointerTag(IndirectPointerTag tag)
V8_INLINE constexpr bool IsValidIndirectPointerTag(IndirectPointerTag tag)
constexpr uint64_t kTrustedPointerTableFreeEntryBit
constexpr int kIndirectPointerTagShift
constexpr uint64_t kIndirectPointerTagMask
constexpr uint64_t kAllTagsForAndBasedTypeChecking[]
V8_INLINE constexpr bool IsTrustedSpaceMigrationInProgressForObjectsWithTag(IndirectPointerTag tag)
constexpr uint64_t kTrustedPointerTableMarkBit
static V8_INLINE constexpr bool IsSharedTrustedPointerType(IndirectPointerTag tag)
V8_INLINE IndirectPointerTag IndirectPointerTagFromInstanceType(InstanceType instance_type)
static V8_INLINE constexpr bool IsPerIsolateTrustedPointerType(IndirectPointerTag tag)
#define DCHECK(condition)
Definition logging.h:482
#define V8_INLINE
Definition v8config.h:500