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 :
38 : namespace v8 {
39 : namespace internal {
40 : namespace trap_handler {
41 :
42 : const size_t kInitialCodeObjectSize = 1024;
43 : const size_t kCodeObjectGrowthFactor = 2;
44 :
45 : constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
46 : return offsetof(CodeProtectionInfo, instructions) +
47 336 : num_protected_instructions * sizeof(ProtectedInstructionData);
48 : }
49 :
50 336 : CodeProtectionInfo* CreateHandlerData(
51 : void* base, size_t size, size_t num_protected_instructions,
52 : ProtectedInstructionData* protected_instructions) {
53 : const size_t alloc_size = HandlerDataSize(num_protected_instructions);
54 : CodeProtectionInfo* data =
55 336 : reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
56 :
57 336 : if (data == nullptr) {
58 : return nullptr;
59 : }
60 :
61 336 : data->base = base;
62 336 : data->size = size;
63 336 : data->num_protected_instructions = num_protected_instructions;
64 :
65 : memcpy(data->instructions, protected_instructions,
66 336 : num_protected_instructions * sizeof(ProtectedInstructionData));
67 :
68 336 : return data;
69 : }
70 :
71 42 : void UpdateHandlerDataCodePointer(int index, void* base) {
72 42 : MetadataLock lock;
73 42 : if (static_cast<size_t>(index) >= gNumCodeObjects) {
74 0 : abort();
75 : }
76 42 : CodeProtectionInfo* data = gCodeObjects[index].code_info;
77 42 : data->base = base;
78 42 : }
79 :
80 336 : int RegisterHandlerData(void* base, size_t size,
81 : size_t num_protected_instructions,
82 : ProtectedInstructionData* protected_instructions) {
83 : // TODO(eholk): in debug builds, make sure this data isn't already registered.
84 :
85 : CodeProtectionInfo* data = CreateHandlerData(
86 336 : base, size, num_protected_instructions, protected_instructions);
87 :
88 336 : if (data == nullptr) {
89 0 : abort();
90 : }
91 :
92 336 : MetadataLock lock;
93 :
94 336 : size_t i = gNextCodeObject;
95 :
96 : // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
97 : // compiler warnings about signed/unsigned comparisons. We aren't worried
98 : // about sign extension because we know std::numeric_limits<int>::max() is
99 : // positive.
100 : const size_t int_max = std::numeric_limits<int>::max();
101 :
102 : // We didn't find an opening in the available space, so grow.
103 336 : if (i == gNumCodeObjects) {
104 : size_t new_size = gNumCodeObjects > 0
105 : ? gNumCodeObjects * kCodeObjectGrowthFactor
106 72 : : kInitialCodeObjectSize;
107 :
108 : // Because we must return an int, there is no point in allocating space for
109 : // more objects than can fit in an int.
110 72 : if (new_size > int_max) {
111 : new_size = int_max;
112 : }
113 72 : if (new_size == gNumCodeObjects) {
114 : return -1;
115 : }
116 :
117 : // Now that we know our new size is valid, we can go ahead and realloc the
118 : // array.
119 : gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
120 72 : realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
121 :
122 72 : if (gCodeObjects == nullptr) {
123 0 : abort();
124 : }
125 :
126 72 : memset(gCodeObjects + gNumCodeObjects, 0,
127 72 : sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
128 72 : gNumCodeObjects = new_size;
129 : }
130 :
131 : DCHECK(gCodeObjects[i].code_info == nullptr);
132 :
133 : // Find out where the next entry should go.
134 336 : if (gCodeObjects[i].next_free == 0) {
135 : // if this is a fresh entry, use the next one.
136 284 : gNextCodeObject = i + 1;
137 : DCHECK(gNextCodeObject == gNumCodeObjects ||
138 : (gCodeObjects[gNextCodeObject].code_info == nullptr &&
139 : gCodeObjects[gNextCodeObject].next_free == 0));
140 : } else {
141 52 : gNextCodeObject = gCodeObjects[i].next_free - 1;
142 : }
143 :
144 336 : if (i <= int_max) {
145 336 : gCodeObjects[i].code_info = data;
146 336 : return static_cast<int>(i);
147 : } else {
148 : return -1;
149 336 : }
150 : }
151 :
152 188 : void ReleaseHandlerData(int index) {
153 : // Remove the data from the global list if it's there.
154 : CodeProtectionInfo* data = nullptr;
155 : {
156 188 : MetadataLock lock;
157 :
158 188 : data = gCodeObjects[index].code_info;
159 188 : gCodeObjects[index].code_info = nullptr;
160 :
161 : // +1 because we reserve {next_entry == 0} to indicate a fresh list entry.
162 188 : gCodeObjects[index].next_free = gNextCodeObject + 1;
163 188 : gNextCodeObject = index;
164 : }
165 : // TODO(eholk): on debug builds, ensure there are no more copies in
166 : // the list.
167 188 : free(data);
168 188 : }
169 :
170 4618 : bool RegisterDefaultSignalHandler() {
171 : #if V8_TRAP_HANDLER_SUPPORTED
172 : struct sigaction action;
173 4618 : action.sa_sigaction = HandleSignal;
174 4618 : action.sa_flags = SA_SIGINFO;
175 4618 : sigemptyset(&action.sa_mask);
176 : // {sigaction} installs a new custom segfault handler. On success, it returns
177 : // 0. If we get a nonzero value, we report an error to the caller by returning
178 : // false.
179 4618 : if (sigaction(SIGSEGV, &action, nullptr) != 0) {
180 : return false;
181 : }
182 :
183 4618 : return true;
184 : #else
185 : return false;
186 : #endif
187 : }
188 :
189 : } // namespace trap_handler
190 : } // namespace internal
191 : } // namespace v8
|