v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
handler-outside.cc
Go to the documentation of this file.
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// PLEASE READ BEFORE CHANGING THIS FILE!
6//
7// This file implements the support code for the out of bounds trap handler.
8// Nothing in here actually runs in the trap handler, but the code here
9// manipulates data structures used by the trap handler so we still need to be
10// careful. In order to minimize this risk, here are some rules to follow.
11//
12// 1. Avoid introducing new external dependencies. The files in src/trap-handler
13// should be as self-contained as possible to make it easy to audit the code.
14//
15// 2. Any changes must be reviewed by someone from the crash reporting
16// or security team. See OWNERS for suggested reviewers.
17//
18// For more information, see https://goo.gl/yMeyUY.
19//
20// For the code that runs in the trap handler itself, see handler-inside.cc.
21
22#include <stddef.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <atomic>
28#include <limits>
29
32
33namespace {
34size_t gNextCodeObject = 0;
35
36#ifdef ENABLE_SLOW_DCHECKS
37constexpr bool kEnableSlowChecks = true;
38#else
39constexpr bool kEnableSlowChecks = false;
40#endif
41} // namespace
42
44
45constexpr size_t kInitialCodeObjectSize = 1024;
46constexpr size_t kCodeObjectGrowthFactor = 2;
47
48constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
49 return offsetof(CodeProtectionInfo, instructions) +
50 num_protected_instructions * sizeof(ProtectedInstructionData);
51}
52
53namespace {
54#ifdef DEBUG
55bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b) {
56 if (a == nullptr || b == nullptr) {
57 return true;
58 }
59 return a->base >= b->base + b->size || b->base >= a->base + a->size;
60}
61#endif
62
63// Verify that the code range does not overlap any that have already been
64// registered.
65void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info) {
66 for (size_t i = 0; i < gNumCodeObjects; ++i) {
67 TH_DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
68 }
69}
70
71void ValidateCodeObjects() {
72 // Sanity-check the code objects
73 for (unsigned i = 0; i < gNumCodeObjects; ++i) {
74 const auto* data = gCodeObjects[i].code_info;
75
76 if (data == nullptr) continue;
77
78 // Do some sanity checks on the protected instruction data
79 for (unsigned j = 0; j < data->num_protected_instructions; ++j) {
80 TH_DCHECK(data->instructions[j].instr_offset >= 0);
81 TH_DCHECK(data->instructions[j].instr_offset < data->size);
82 }
83 }
84
85 // Check the validity of the free list.
86#ifdef DEBUG
87 size_t free_count = 0;
88 for (size_t i = gNextCodeObject; i != gNumCodeObjects;
91 ++free_count;
92 // This check will fail if we encounter a cycle.
93 TH_DCHECK(free_count <= gNumCodeObjects);
94 }
95
96 // Check that all free entries are reachable via the free list.
97 size_t free_count2 = 0;
98 for (size_t i = 0; i < gNumCodeObjects; ++i) {
99 if (gCodeObjects[i].code_info == nullptr) {
100 ++free_count2;
101 }
102 }
103 TH_DCHECK(free_count == free_count2);
104#endif
105}
106} // namespace
107
109 uintptr_t base, size_t size, size_t num_protected_instructions,
110 const ProtectedInstructionData* protected_instructions) {
111 const size_t alloc_size = HandlerDataSize(num_protected_instructions);
112 CodeProtectionInfo* data =
113 reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
114
115 if (data == nullptr) {
116 return nullptr;
117 }
118
119 data->base = base;
120 data->size = size;
121 data->num_protected_instructions = num_protected_instructions;
122
123 if (num_protected_instructions > 0) {
124 memcpy(data->instructions, protected_instructions,
125 num_protected_instructions * sizeof(ProtectedInstructionData));
126 }
127
128 return data;
129}
130
132 uintptr_t base, size_t size, size_t num_protected_instructions,
133 const ProtectedInstructionData* protected_instructions) {
135 base, size, num_protected_instructions, protected_instructions);
136
137 if (data == nullptr) {
138 abort();
139 }
140
141 MetadataLock lock;
142
143 if (kEnableSlowChecks) {
144 VerifyCodeRangeIsDisjoint(data);
145 }
146
147 size_t i = gNextCodeObject;
148
149 // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
150 // compiler warnings about signed/unsigned comparisons. We aren't worried
151 // about sign extension because we know std::numeric_limits<int>::max() is
152 // positive.
153 const size_t int_max = std::numeric_limits<int>::max();
154
155 // We didn't find an opening in the available space, so grow.
156 if (i == gNumCodeObjects) {
157 size_t new_size = gNumCodeObjects > 0
160
161 // Because we must return an int, there is no point in allocating space for
162 // more objects than can fit in an int.
163 if (new_size > int_max) {
164 new_size = int_max;
165 }
166 if (new_size == gNumCodeObjects) {
167 free(data);
168 return kInvalidIndex;
169 }
170
171 // Now that we know our new size is valid, we can go ahead and realloc the
172 // array.
174 realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
175
176 if (gCodeObjects == nullptr) {
177 abort();
178 }
179
180 memset(gCodeObjects + gNumCodeObjects, 0,
181 sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
182 for (size_t j = gNumCodeObjects; j < new_size; ++j) {
183 gCodeObjects[j].next_free = j + 1;
184 }
185 gNumCodeObjects = new_size;
186 }
187
188 TH_DCHECK(gCodeObjects[i].code_info == nullptr);
189
190 // Find out where the next entry should go.
191 gNextCodeObject = gCodeObjects[i].next_free;
192
193 if (i <= int_max) {
195
196 if (kEnableSlowChecks) {
197 ValidateCodeObjects();
198 }
199
200 return static_cast<int>(i);
201 } else {
202 free(data);
203 return kInvalidIndex;
204 }
205}
206
207void ReleaseHandlerData(int index) {
208 if (index == kInvalidIndex) {
209 return;
210 }
211 TH_DCHECK(index >= 0);
212
213 // Remove the data from the global list if it's there.
214 CodeProtectionInfo* data = nullptr;
215 {
216 MetadataLock lock;
217
219 gCodeObjects[index].code_info = nullptr;
220
221 gCodeObjects[index].next_free = gNextCodeObject;
222 gNextCodeObject = index;
223
224 if (kEnableSlowChecks) {
225 ValidateCodeObjects();
226 }
227 }
228 // TODO(eholk): on debug builds, ensure there are no more copies in
229 // the list.
230 TH_DCHECK(data); // make sure we're releasing legitimate handler data.
231 free(data);
232}
233
234bool RegisterV8Sandbox(uintptr_t base, size_t size) {
236
237#ifdef DEBUG
238 for (SandboxRecord* current = gSandboxRecordsHead; current != nullptr;
239 current = current->next) {
240 TH_DCHECK(current->base != base);
241 }
242#endif
243
244 SandboxRecord* new_record =
245 reinterpret_cast<SandboxRecord*>(malloc(sizeof(SandboxRecord)));
246 if (new_record == nullptr) {
247 return false;
248 }
249
250 new_record->base = base;
251 new_record->size = size;
252 new_record->next = gSandboxRecordsHead;
253 gSandboxRecordsHead = new_record;
254 return true;
255}
256
257void UnregisterV8Sandbox(uintptr_t base, size_t size) {
259
261 SandboxRecord* previous = nullptr;
262 while (current != nullptr) {
263 if (current->base == base) {
264 break;
265 }
267 current = current->next;
268 }
269
270 TH_CHECK(current != nullptr);
271 TH_CHECK(current->size == size);
272 if (previous) {
273 previous->next = current->next;
274 } else {
275 gSandboxRecordsHead = current->next;
276 }
277 free(current);
278}
279
281
283 return gRecoveredTrapCount.load(std::memory_order_relaxed);
284}
285
286#if !V8_TRAP_HANDLER_SUPPORTED
287// This version is provided for systems that do not support trap handlers.
288// Otherwise, the correct one should be implemented in the appropriate
289// platform-specific handler-outside.cc.
290bool RegisterDefaultTrapHandler() { return false; }
291
293#endif
294
296std::atomic<bool> g_can_enable_trap_handler{true};
297
298bool EnableTrapHandler(bool use_v8_handler) {
299 // We should only enable the trap handler once, and before any call to
300 // {IsTrapHandlerEnabled}. Enabling the trap handler late can lead to problems
301 // because code or objects might have been generated under the assumption that
302 // trap handlers are disabled.
303 bool can_enable =
304 g_can_enable_trap_handler.exchange(false, std::memory_order_relaxed);
305 // EnableTrapHandler called twice, or after IsTrapHandlerEnabled.
306 TH_CHECK(can_enable);
308 return false;
309 }
310 if (use_v8_handler) {
313 }
315 return true;
316}
317
318void SetLandingPad(uintptr_t landing_pad) { gLandingPad.store(landing_pad); }
319
320#if defined(BUILDING_V8_SHARED_PRIVATE) || defined(USING_V8_SHARED_PRIVATE)
323}
324#endif
325
326} // namespace v8::internal::trap_handler
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
LineAndColumn current
LineAndColumn previous
constexpr size_t HandlerDataSize(size_t num_protected_instructions)
constexpr size_t kInitialCodeObjectSize
std::atomic< bool > g_can_enable_trap_handler
SandboxRecord * gSandboxRecordsHead
CodeProtectionInfoListEntry * gCodeObjects
thread_local int g_thread_in_wasm_code
void SetLandingPad(uintptr_t landing_pad)
int RegisterHandlerData(uintptr_t base, size_t size, size_t num_protected_instructions, const ProtectedInstructionData *protected_instructions)
CodeProtectionInfo * CreateHandlerData(uintptr_t base, size_t size, size_t num_protected_instructions, const ProtectedInstructionData *protected_instructions)
constexpr size_t kCodeObjectGrowthFactor
std::atomic< uintptr_t > gLandingPad
bool EnableTrapHandler(bool use_v8_handler)
void UnregisterV8Sandbox(uintptr_t base, size_t size)
std::atomic_size_t gRecoveredTrapCount
bool RegisterV8Sandbox(uintptr_t base, size_t size)
#define V8_TRAP_HANDLER_SUPPORTED
#define TH_DCHECK(condition)
#define TH_CHECK(condition)