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