Home | History | Annotate | Download | only in trap-handler
      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