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 signal handler.
8 : // Nothing in here actually runs in the signal handler, but the code here
9 : // manipulates data structures used by the signal 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. Se OWNERS for suggested reviewers.
17 : //
18 : // For more information, see https://goo.gl/yMeyUY.
19 : //
20 : // For the code that runs in the signal handler itself, see handler-inside.cc.
21 :
22 : #include <signal.h>
23 : #include <stddef.h>
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 :
28 : #include <atomic>
29 : #include <limits>
30 :
31 : #include "src/trap-handler/trap-handler-internal.h"
32 : #include "src/trap-handler/trap-handler.h"
33 :
34 : namespace {
35 : size_t gNextCodeObject = 0;
36 :
37 : #if defined(DEBUG)
38 : const bool kEnableDebug = true;
39 : #else
40 : const bool kEnableDebug = false;
41 : #endif
42 : }
43 :
44 : namespace v8 {
45 : namespace internal {
46 : namespace trap_handler {
47 :
48 : const size_t kInitialCodeObjectSize = 1024;
49 : const size_t kCodeObjectGrowthFactor = 2;
50 :
51 : constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
52 743 : return offsetof(CodeProtectionInfo, instructions) +
53 743 : num_protected_instructions * sizeof(ProtectedInstructionData);
54 : }
55 :
56 : namespace {
57 : template <typename = std::enable_if<kEnableDebug>>
58 : bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b) {
59 : if (a == nullptr || b == nullptr) {
60 : return true;
61 : }
62 :
63 : const auto a_base = reinterpret_cast<uintptr_t>(a->base);
64 : const auto b_base = reinterpret_cast<uintptr_t>(b->base);
65 :
66 : return a_base >= b_base + b->size || b_base >= a_base + a->size;
67 : }
68 :
69 : // Verify that the code range does not overlap any that have already been
70 : // registered.
71 : void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info) {
72 : for (size_t i = 0; i < gNumCodeObjects; ++i) {
73 : DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
74 : }
75 : }
76 :
77 : void ValidateCodeObjects() {
78 : // Sanity-check the code objects
79 : for (unsigned i = 0; i < gNumCodeObjects; ++i) {
80 : const auto* data = gCodeObjects[i].code_info;
81 :
82 : if (data == nullptr) continue;
83 :
84 : // Do some sanity checks on the protected instruction data
85 : for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
86 : DCHECK_GE(data->instructions[i].instr_offset, 0);
87 : DCHECK_LT(data->instructions[i].instr_offset, data->size);
88 : DCHECK_GE(data->instructions[i].landing_offset, 0);
89 : DCHECK_LT(data->instructions[i].landing_offset, data->size);
90 : DCHECK_GT(data->instructions[i].landing_offset,
91 : data->instructions[i].instr_offset);
92 : }
93 : }
94 :
95 : // Check the validity of the free list.
96 : size_t free_count = 0;
97 : for (size_t i = gNextCodeObject; i != gNumCodeObjects;
98 : i = gCodeObjects[i].next_free) {
99 : DCHECK_LT(i, gNumCodeObjects);
100 : ++free_count;
101 : // This check will fail if we encounter a cycle.
102 : DCHECK_LE(free_count, gNumCodeObjects);
103 : }
104 :
105 : // Check that all free entries are reachable via the free list.
106 : size_t free_count2 = 0;
107 : for (size_t i = 0; i < gNumCodeObjects; ++i) {
108 : if (gCodeObjects[i].code_info == nullptr) {
109 : ++free_count2;
110 : }
111 : }
112 : DCHECK_EQ(free_count, free_count2);
113 : }
114 : } // namespace
115 :
116 743 : CodeProtectionInfo* CreateHandlerData(
117 : void* base, size_t size, size_t num_protected_instructions,
118 : ProtectedInstructionData* protected_instructions) {
119 : const size_t alloc_size = HandlerDataSize(num_protected_instructions);
120 : CodeProtectionInfo* data =
121 743 : reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
122 :
123 743 : if (data == nullptr) {
124 : return nullptr;
125 : }
126 :
127 743 : data->base = base;
128 743 : data->size = size;
129 743 : data->num_protected_instructions = num_protected_instructions;
130 :
131 : memcpy(data->instructions, protected_instructions,
132 743 : num_protected_instructions * sizeof(ProtectedInstructionData));
133 :
134 743 : return data;
135 : }
136 :
137 7 : void UpdateHandlerDataCodePointer(int index, void* base) {
138 7 : MetadataLock lock;
139 7 : if (static_cast<size_t>(index) >= gNumCodeObjects) {
140 0 : abort();
141 : }
142 7 : CodeProtectionInfo* data = gCodeObjects[index].code_info;
143 7 : data->base = base;
144 7 : }
145 :
146 743 : int RegisterHandlerData(void* base, size_t size,
147 : size_t num_protected_instructions,
148 : ProtectedInstructionData* protected_instructions) {
149 : // TODO(eholk): in debug builds, make sure this data isn't already registered.
150 :
151 : CodeProtectionInfo* data = CreateHandlerData(
152 743 : base, size, num_protected_instructions, protected_instructions);
153 :
154 743 : if (data == nullptr) {
155 0 : abort();
156 : }
157 :
158 743 : MetadataLock lock;
159 :
160 : if (kEnableDebug) {
161 : VerifyCodeRangeIsDisjoint(data);
162 : }
163 :
164 743 : size_t i = gNextCodeObject;
165 :
166 : // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
167 : // compiler warnings about signed/unsigned comparisons. We aren't worried
168 : // about sign extension because we know std::numeric_limits<int>::max() is
169 : // positive.
170 : const size_t int_max = std::numeric_limits<int>::max();
171 :
172 : // We didn't find an opening in the available space, so grow.
173 743 : if (i == gNumCodeObjects) {
174 : size_t new_size = gNumCodeObjects > 0
175 : ? gNumCodeObjects * kCodeObjectGrowthFactor
176 147 : : kInitialCodeObjectSize;
177 :
178 : // Because we must return an int, there is no point in allocating space for
179 : // more objects than can fit in an int.
180 147 : if (new_size > int_max) {
181 : new_size = int_max;
182 : }
183 147 : if (new_size == gNumCodeObjects) {
184 : return kInvalidIndex;
185 : }
186 :
187 : // Now that we know our new size is valid, we can go ahead and realloc the
188 : // array.
189 : gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
190 147 : realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
191 :
192 147 : if (gCodeObjects == nullptr) {
193 0 : abort();
194 : }
195 :
196 147 : memset(gCodeObjects + gNumCodeObjects, 0,
197 147 : sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
198 150822 : for (size_t j = gNumCodeObjects; j < new_size; ++j) {
199 150528 : gCodeObjects[j].next_free = j + 1;
200 : }
201 147 : gNumCodeObjects = new_size;
202 : }
203 :
204 : DCHECK(gCodeObjects[i].code_info == nullptr);
205 :
206 : // Find out where the next entry should go.
207 743 : gNextCodeObject = gCodeObjects[i].next_free;
208 :
209 743 : if (i <= int_max) {
210 743 : gCodeObjects[i].code_info = data;
211 :
212 : if (kEnableDebug) {
213 : ValidateCodeObjects();
214 : }
215 :
216 743 : return static_cast<int>(i);
217 : } else {
218 : return kInvalidIndex;
219 743 : }
220 : }
221 :
222 6528 : void ReleaseHandlerData(int index) {
223 6528 : if (index == kInvalidIndex) {
224 6528 : return;
225 : }
226 : DCHECK_GE(index, 0);
227 :
228 : // Remove the data from the global list if it's there.
229 : CodeProtectionInfo* data = nullptr;
230 : {
231 743 : MetadataLock lock;
232 :
233 743 : data = gCodeObjects[index].code_info;
234 743 : gCodeObjects[index].code_info = nullptr;
235 :
236 743 : gCodeObjects[index].next_free = gNextCodeObject;
237 743 : gNextCodeObject = index;
238 :
239 : if (kEnableDebug) {
240 : ValidateCodeObjects();
241 743 : }
242 : }
243 : // TODO(eholk): on debug builds, ensure there are no more copies in
244 : // the list.
245 : DCHECK_NOT_NULL(data); // make sure we're releasing legitimate handler data.
246 743 : free(data);
247 : }
248 :
249 5131 : bool RegisterDefaultSignalHandler() {
250 : #if V8_TRAP_HANDLER_SUPPORTED
251 5131 : CHECK(!g_is_default_signal_handler_registered);
252 :
253 : struct sigaction action;
254 5131 : action.sa_sigaction = HandleSignal;
255 5131 : action.sa_flags = SA_SIGINFO;
256 5131 : sigemptyset(&action.sa_mask);
257 : // {sigaction} installs a new custom segfault handler. On success, it returns
258 : // 0. If we get a nonzero value, we report an error to the caller by returning
259 : // false.
260 5131 : if (sigaction(SIGSEGV, &action, &g_old_handler) != 0) {
261 : return false;
262 : }
263 :
264 5131 : g_is_default_signal_handler_registered = true;
265 5131 : return true;
266 : #else
267 : return false;
268 : #endif
269 : }
270 :
271 11022 : size_t GetRecoveredTrapCount() {
272 11022 : return gRecoveredTrapCount.load(std::memory_order_relaxed);
273 : }
274 :
275 : } // namespace trap_handler
276 : } // namespace internal
277 : } // namespace v8
|