text: // Copyright 2017 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  // PLEASE READ BEFORE CHANGING THIS FILE!
  //
  // This file implements the support code for the out of bounds signal handler.
  // Nothing in here actually runs in the signal handler, but the code here
  // manipulates data structures used by the signal handler so we still need to be
  // careful. In order to minimize this risk, here are some rules to follow.
  //
  // 1. Avoid introducing new external dependencies. The files in src/trap-handler
  //    should be as self-contained as possible to make it easy to audit the code.
  //
  // 2. Any changes must be reviewed by someone from the crash reporting
  //    or security team. Se OWNERS for suggested reviewers.
  //
  // For more information, see https://goo.gl/yMeyUY.
  //
  // For the code that runs in the signal handler itself, see handler-inside.cc.
  
  #include <signal.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include <atomic>
  #include <limits>
  
  #include "src/trap-handler/trap-handler-internal.h"
  #include "src/trap-handler/trap-handler.h"
  
  namespace {
  size_t gNextCodeObject = 0;
  
  #ifdef DEBUG
  constexpr bool kEnableDebug = true;
  #else
  constexpr bool kEnableDebug = false;
  #endif
  }
  
  namespace v8 {
  namespace internal {
  namespace trap_handler {
  
  constexpr size_t kInitialCodeObjectSize = 1024;
  constexpr size_t kCodeObjectGrowthFactor = 2;
  
  constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
    return offsetof(CodeProtectionInfo, instructions) +
           num_protected_instructions * sizeof(ProtectedInstructionData);
  }
  
  namespace {
  #ifdef DEBUG
  bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b) {
    if (a == nullptr || b == nullptr) {
      return true;
    }
    return a->base >= b->base + b->size || b->base >= a->base + a->size;
  }
  #endif
  
  // Verify that the code range does not overlap any that have already been
  // registered.
  void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info) {
    for (size_t i = 0; i < gNumCodeObjects; ++i) {
      DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
    }
  }
  
  void ValidateCodeObjects() {
    // Sanity-check the code objects
    for (unsigned i = 0; i < gNumCodeObjects; ++i) {
      const auto* data = gCodeObjects[i].code_info;
  
      if (data == nullptr) continue;
  
      // Do some sanity checks on the protected instruction data
      for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
        DCHECK_GE(data->instructions[i].instr_offset, 0);
        DCHECK_LT(data->instructions[i].instr_offset, data->size);
        DCHECK_GE(data->instructions[i].landing_offset, 0);
        DCHECK_LT(data->instructions[i].landing_offset, data->size);
        DCHECK_GT(data->instructions[i].landing_offset,
                  data->instructions[i].instr_offset);
      }
    }
  
    // Check the validity of the free list.
    size_t free_count = 0;
    for (size_t i = gNextCodeObject; i != gNumCodeObjects;
         i = gCodeObjects[i].next_free) {
      DCHECK_LT(i, gNumCodeObjects);
      ++free_count;
      // This check will fail if we encounter a cycle.
      DCHECK_LE(free_count, gNumCodeObjects);
    }
  
    // Check that all free entries are reachable via the free list.
    size_t free_count2 = 0;
    for (size_t i = 0; i < gNumCodeObjects; ++i) {
      if (gCodeObjects[i].code_info == nullptr) {
        ++free_count2;
      }
    }
    DCHECK_EQ(free_count, free_count2);
  }
  }  // namespace
  
  CodeProtectionInfo* CreateHandlerData(
      Address base, size_t size, size_t num_protected_instructions,
      const ProtectedInstructionData* protected_instructions) {
    const size_t alloc_size = HandlerDataSize(num_protected_instructions);
    CodeProtectionInfo* data =
        reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
  
    if (data == nullptr) {
      return nullptr;
    }
  
    data->base = base;
    data->size = size;
    data->num_protected_instructions = num_protected_instructions;
  
    memcpy(data->instructions, protected_instructions,
           num_protected_instructions * sizeof(ProtectedInstructionData));
  
    return data;
  }
  
  int RegisterHandlerData(
      Address base, size_t size, size_t num_protected_instructions,
      const ProtectedInstructionData* protected_instructions) {
  
    CodeProtectionInfo* data = CreateHandlerData(
        base, size, num_protected_instructions, protected_instructions);
  
    if (data == nullptr) {
      abort();
    }
  
    MetadataLock lock;
  
    if (kEnableDebug) {
      VerifyCodeRangeIsDisjoint(data);
    }
  
    size_t i = gNextCodeObject;
  
    // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
    // compiler warnings about signed/unsigned comparisons. We aren't worried
    // about sign extension because we know std::numeric_limits<int>::max() is
    // positive.
    const size_t int_max = std::numeric_limits<int>::max();
  
    // We didn't find an opening in the available space, so grow.
    if (i == gNumCodeObjects) {
      size_t new_size = gNumCodeObjects > 0
                            ? gNumCodeObjects * kCodeObjectGrowthFactor
                            : kInitialCodeObjectSize;
  
      // Because we must return an int, there is no point in allocating space for
      // more objects than can fit in an int.
      if (new_size > int_max) {
        new_size = int_max;
      }
      if (new_size == gNumCodeObjects) {
        free(data);
        return kInvalidIndex;
      }
  
      // Now that we know our new size is valid, we can go ahead and realloc the
      // array.
      gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
          realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));
  
      if (gCodeObjects == nullptr) {
        abort();
      }
  
      memset(gCodeObjects + gNumCodeObjects, 0,
             sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
      for (size_t j = gNumCodeObjects; j < new_size; ++j) {
        gCodeObjects[j].next_free = j + 1;
      }
      gNumCodeObjects = new_size;
    }
  
    DCHECK(gCodeObjects[i].code_info == nullptr);
  
    // Find out where the next entry should go.
    gNextCodeObject = gCodeObjects[i].next_free;
  
    if (i <= int_max) {
      gCodeObjects[i].code_info = data;
  
      if (kEnableDebug) {
        ValidateCodeObjects();
      }
  
      return static_cast<int>(i);
    } else {
      free(data);
      return kInvalidIndex;
    }
  }
  
  void ReleaseHandlerData(int index) {
    if (index == kInvalidIndex) {
      return;
    }
    DCHECK_GE(index, 0);
  
    // Remove the data from the global list if it's there.
    CodeProtectionInfo* data = nullptr;
    {
      MetadataLock lock;
  
      data = gCodeObjects[index].code_info;
      gCodeObjects[index].code_info = nullptr;
  
      gCodeObjects[index].next_free = gNextCodeObject;
      gNextCodeObject = index;
  
      if (kEnableDebug) {
        ValidateCodeObjects();
      }
    }
    // TODO(eholk): on debug builds, ensure there are no more copies in
    // the list.
    DCHECK_NOT_NULL(data);  // make sure we're releasing legitimate handler data.
    free(data);
  }
  
  size_t GetRecoveredTrapCount() {
    return gRecoveredTrapCount.load(std::memory_order_relaxed);
  }
  
  #if !V8_TRAP_HANDLER_SUPPORTED
  // This version is provided for systems that do not support trap handlers.
  // Otherwise, the correct one should be implemented in the appropriate
  // platform-specific handler-outside.cc.
  bool RegisterDefaultTrapHandler() { return false; }
  #endif
  
  bool g_is_trap_handler_enabled;
  
  bool EnableTrapHandler(bool use_v8_signal_handler) {
    if (!V8_TRAP_HANDLER_SUPPORTED) {
      return false;
    }
    if (use_v8_signal_handler) {
      g_is_trap_handler_enabled = RegisterDefaultTrapHandler();
      return g_is_trap_handler_enabled;
    }
    g_is_trap_handler_enabled = true;
    return true;
  }
  
  }  // namespace trap_handler
  }  // namespace internal
  }  // namespace v8
  