Line data Source code
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 :
30 : #include "src/trap-handler/trap-handler-internal.h"
31 : #include "src/trap-handler/trap-handler.h"
32 :
33 : namespace {
34 : size_t gNextCodeObject = 0;
35 :
36 : #ifdef ENABLE_SLOW_DCHECKS
37 : constexpr bool kEnableSlowChecks = true;
38 : #else
39 : constexpr bool kEnableSlowChecks = false;
40 : #endif
41 : }
42 :
43 : namespace v8 {
44 : namespace internal {
45 : namespace trap_handler {
46 :
47 : constexpr size_t kInitialCodeObjectSize = 1024;
48 : constexpr size_t kCodeObjectGrowthFactor = 2;
49 :
50 : constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
51 68427 : return offsetof(CodeProtectionInfo, instructions) +
52 68427 : num_protected_instructions * sizeof(ProtectedInstructionData);
53 : }
54 :
55 : namespace {
56 : #ifdef DEBUG
57 : bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b) {
58 : if (a == nullptr || b == nullptr) {
59 : return true;
60 : }
61 : return a->base >= b->base + b->size || b->base >= a->base + a->size;
62 : }
63 : #endif
64 :
65 : // Verify that the code range does not overlap any that have already been
66 : // registered.
67 : void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info) {
68 : for (size_t i = 0; i < gNumCodeObjects; ++i) {
69 : DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
70 : }
71 : }
72 :
73 : void ValidateCodeObjects() {
74 : // Sanity-check the code objects
75 : for (unsigned i = 0; i < gNumCodeObjects; ++i) {
76 : const auto* data = gCodeObjects[i].code_info;
77 :
78 : if (data == nullptr) continue;
79 :
80 : // Do some sanity checks on the protected instruction data
81 : for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
82 : DCHECK_GE(data->instructions[i].instr_offset, 0);
83 : DCHECK_LT(data->instructions[i].instr_offset, data->size);
84 : DCHECK_GE(data->instructions[i].landing_offset, 0);
85 : DCHECK_LT(data->instructions[i].landing_offset, data->size);
86 : DCHECK_GT(data->instructions[i].landing_offset,
87 : data->instructions[i].instr_offset);
88 : }
89 : }
90 :
91 : // Check the validity of the free list.
92 : size_t free_count = 0;
93 : for (size_t i = gNextCodeObject; i != gNumCodeObjects;
94 : i = gCodeObjects[i].next_free) {
95 : DCHECK_LT(i, gNumCodeObjects);
96 : ++free_count;
97 : // This check will fail if we encounter a cycle.
98 : DCHECK_LE(free_count, gNumCodeObjects);
99 : }
100 :
101 : // Check that all free entries are reachable via the free list.
102 : size_t free_count2 = 0;
103 : for (size_t i = 0; i < gNumCodeObjects; ++i) {
104 : if (gCodeObjects[i].code_info == nullptr) {
105 : ++free_count2;
106 : }
107 : }
108 : DCHECK_EQ(free_count, free_count2);
109 : }
110 : } // namespace
111 :
112 68427 : CodeProtectionInfo* CreateHandlerData(
113 : Address base, size_t size, size_t num_protected_instructions,
114 : const ProtectedInstructionData* protected_instructions) {
115 : const size_t alloc_size = HandlerDataSize(num_protected_instructions);
116 : CodeProtectionInfo* data =
117 68427 : reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
118 :
119 68427 : if (data == nullptr) {
120 : return nullptr;
121 : }
122 :
123 68427 : data->base = base;
124 68427 : data->size = size;
125 68427 : data->num_protected_instructions = num_protected_instructions;
126 :
127 : memcpy(data->instructions, protected_instructions,
128 68427 : num_protected_instructions * sizeof(ProtectedInstructionData));
129 :
130 68427 : return data;
131 : }
132 :
133 68427 : int RegisterHandlerData(
134 : Address base, size_t size, size_t num_protected_instructions,
135 : const ProtectedInstructionData* protected_instructions) {
136 :
137 : CodeProtectionInfo* data = CreateHandlerData(
138 68427 : base, size, num_protected_instructions, protected_instructions);
139 :
140 68427 : if (data == nullptr) {
141 0 : abort();
142 : }
143 :
144 68427 : MetadataLock lock;
145 :
146 : if (kEnableSlowChecks) {
147 : VerifyCodeRangeIsDisjoint(data);
148 : }
149 :
150 68427 : size_t i = gNextCodeObject;
151 :
152 : // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
153 : // compiler warnings about signed/unsigned comparisons. We aren't worried
154 : // about sign extension because we know std::numeric_limits<int>::max() is
155 : // positive.
156 : const size_t int_max = std::numeric_limits<int>::max();
157 :
158 : // We didn't find an opening in the available space, so grow.
159 68427 : if (i == gNumCodeObjects) {
160 : size_t new_size = gNumCodeObjects > 0
161 : ? gNumCodeObjects * kCodeObjectGrowthFactor
162 982 : : kInitialCodeObjectSize;
163 :
164 : // Because we must return an int, there is no point in allocating space for
165 : // more objects than can fit in an int.
166 982 : if (new_size > int_max) {
167 : new_size = int_max;
168 : }
169 982 : if (new_size == gNumCodeObjects) {
170 0 : free(data);
171 0 : return kInvalidIndex;
172 : }
173 :
174 : // Now that we know our new size is valid, we can go ahead and realloc the
175 : // array.
176 : gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
177 982 : realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
178 :
179 982 : if (gCodeObjects == nullptr) {
180 0 : abort();
181 : }
182 :
183 982 : memset(gCodeObjects + gNumCodeObjects, 0,
184 982 : sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
185 1015724 : for (size_t j = gNumCodeObjects; j < new_size; ++j) {
186 1013760 : gCodeObjects[j].next_free = j + 1;
187 : }
188 982 : gNumCodeObjects = new_size;
189 : }
190 :
191 : DCHECK(gCodeObjects[i].code_info == nullptr);
192 :
193 : // Find out where the next entry should go.
194 68427 : gNextCodeObject = gCodeObjects[i].next_free;
195 :
196 68427 : if (i <= int_max) {
197 68427 : gCodeObjects[i].code_info = data;
198 :
199 : if (kEnableSlowChecks) {
200 : ValidateCodeObjects();
201 : }
202 :
203 68427 : return static_cast<int>(i);
204 : } else {
205 0 : free(data);
206 0 : return kInvalidIndex;
207 68427 : }
208 : }
209 :
210 68417 : void ReleaseHandlerData(int index) {
211 68417 : if (index == kInvalidIndex) {
212 68417 : return;
213 : }
214 : DCHECK_GE(index, 0);
215 :
216 : // Remove the data from the global list if it's there.
217 : CodeProtectionInfo* data = nullptr;
218 : {
219 68417 : MetadataLock lock;
220 :
221 68417 : data = gCodeObjects[index].code_info;
222 68417 : gCodeObjects[index].code_info = nullptr;
223 :
224 68417 : gCodeObjects[index].next_free = gNextCodeObject;
225 68417 : gNextCodeObject = index;
226 :
227 : if (kEnableSlowChecks) {
228 : ValidateCodeObjects();
229 68417 : }
230 : }
231 : // TODO(eholk): on debug builds, ensure there are no more copies in
232 : // the list.
233 : DCHECK_NOT_NULL(data); // make sure we're releasing legitimate handler data.
234 68417 : free(data);
235 : }
236 :
237 84476 : int* GetThreadInWasmThreadLocalAddress() { return &g_thread_in_wasm_code; }
238 :
239 16032 : size_t GetRecoveredTrapCount() {
240 16032 : return gRecoveredTrapCount.load(std::memory_order_relaxed);
241 : }
242 :
243 : #if !V8_TRAP_HANDLER_SUPPORTED
244 : // This version is provided for systems that do not support trap handlers.
245 : // Otherwise, the correct one should be implemented in the appropriate
246 : // platform-specific handler-outside.cc.
247 : bool RegisterDefaultTrapHandler() { return false; }
248 :
249 : void RemoveTrapHandler() {}
250 : #endif
251 :
252 : bool g_is_trap_handler_enabled;
253 :
254 55426 : bool EnableTrapHandler(bool use_v8_handler) {
255 : if (!V8_TRAP_HANDLER_SUPPORTED) {
256 : return false;
257 : }
258 55426 : if (use_v8_handler) {
259 55420 : g_is_trap_handler_enabled = RegisterDefaultTrapHandler();
260 55420 : return g_is_trap_handler_enabled;
261 : }
262 6 : g_is_trap_handler_enabled = true;
263 6 : return true;
264 : }
265 :
266 : } // namespace trap_handler
267 : } // namespace internal
268 : } // namespace v8
|