V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
handler-outside.cc
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. Se 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 DEBUG
37 constexpr bool kEnableDebug = true;
38 #else
39 constexpr bool kEnableDebug = 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  return offsetof(CodeProtectionInfo, instructions) +
52  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 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  reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
118 
119  if (data == nullptr) {
120  return nullptr;
121  }
122 
123  data->base = base;
124  data->size = size;
125  data->num_protected_instructions = num_protected_instructions;
126 
127  memcpy(data->instructions, protected_instructions,
128  num_protected_instructions * sizeof(ProtectedInstructionData));
129 
130  return data;
131 }
132 
133 int RegisterHandlerData(
134  Address base, size_t size, size_t num_protected_instructions,
135  const ProtectedInstructionData* protected_instructions) {
136 
137  CodeProtectionInfo* data = CreateHandlerData(
138  base, size, num_protected_instructions, protected_instructions);
139 
140  if (data == nullptr) {
141  abort();
142  }
143 
144  MetadataLock lock;
145 
146  if (kEnableDebug) {
147  VerifyCodeRangeIsDisjoint(data);
148  }
149 
150  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  if (i == gNumCodeObjects) {
160  size_t new_size = gNumCodeObjects > 0
161  ? gNumCodeObjects * kCodeObjectGrowthFactor
162  : 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  if (new_size > int_max) {
167  new_size = int_max;
168  }
169  if (new_size == gNumCodeObjects) {
170  free(data);
171  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  realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
178 
179  if (gCodeObjects == nullptr) {
180  abort();
181  }
182 
183  memset(gCodeObjects + gNumCodeObjects, 0,
184  sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
185  for (size_t j = gNumCodeObjects; j < new_size; ++j) {
186  gCodeObjects[j].next_free = j + 1;
187  }
188  gNumCodeObjects = new_size;
189  }
190 
191  DCHECK(gCodeObjects[i].code_info == nullptr);
192 
193  // Find out where the next entry should go.
194  gNextCodeObject = gCodeObjects[i].next_free;
195 
196  if (i <= int_max) {
197  gCodeObjects[i].code_info = data;
198 
199  if (kEnableDebug) {
200  ValidateCodeObjects();
201  }
202 
203  return static_cast<int>(i);
204  } else {
205  free(data);
206  return kInvalidIndex;
207  }
208 }
209 
210 void ReleaseHandlerData(int index) {
211  if (index == kInvalidIndex) {
212  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  MetadataLock lock;
220 
221  data = gCodeObjects[index].code_info;
222  gCodeObjects[index].code_info = nullptr;
223 
224  gCodeObjects[index].next_free = gNextCodeObject;
225  gNextCodeObject = index;
226 
227  if (kEnableDebug) {
228  ValidateCodeObjects();
229  }
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  free(data);
235 }
236 
237 size_t GetRecoveredTrapCount() {
238  return gRecoveredTrapCount.load(std::memory_order_relaxed);
239 }
240 
241 #if !V8_TRAP_HANDLER_SUPPORTED
242 // This version is provided for systems that do not support trap handlers.
243 // Otherwise, the correct one should be implemented in the appropriate
244 // platform-specific handler-outside.cc.
245 bool RegisterDefaultTrapHandler() { return false; }
246 
247 void RemoveTrapHandler() {}
248 #endif
249 
250 bool g_is_trap_handler_enabled;
251 
252 bool EnableTrapHandler(bool use_v8_handler) {
253  if (!V8_TRAP_HANDLER_SUPPORTED) {
254  return false;
255  }
256  if (use_v8_handler) {
257  g_is_trap_handler_enabled = RegisterDefaultTrapHandler();
258  return g_is_trap_handler_enabled;
259  }
260  g_is_trap_handler_enabled = true;
261  return true;
262 }
263 
264 } // namespace trap_handler
265 } // namespace internal
266 } // namespace v8
Definition: libplatform.h:13