LCOV - code coverage report
Current view: top level - src/trap-handler - handler-outside.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 57 60 95.0 %
Date: 2017-10-20 Functions: 6 6 100.0 %

          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

Generated by: LCOV version 1.10