summaryrefslogtreecommitdiff
path: root/deps/v8/src/trap-handler/handler-outside.cc
blob: ea1500e67e67ecbff6cbb528c0ef996bb5ecb0f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// 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;
}

namespace v8 {
namespace internal {
namespace trap_handler {

const size_t kInitialCodeObjectSize = 1024;
const size_t kCodeObjectGrowthFactor = 2;

constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
  return offsetof(CodeProtectionInfo, instructions) +
         num_protected_instructions * sizeof(ProtectedInstructionData);
}

CodeProtectionInfo* CreateHandlerData(
    void* base, size_t size, size_t num_protected_instructions,
    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;
}

void UpdateHandlerDataCodePointer(int index, void* base) {
  MetadataLock lock;
  if (static_cast<size_t>(index) >= gNumCodeObjects) {
    abort();
  }
  CodeProtectionInfo* data = gCodeObjects[index].code_info;
  data->base = base;
}

int RegisterHandlerData(void* base, size_t size,
                        size_t num_protected_instructions,
                        ProtectedInstructionData* protected_instructions) {
  // TODO(eholk): in debug builds, make sure this data isn't already registered.

  CodeProtectionInfo* data = CreateHandlerData(
      base, size, num_protected_instructions, protected_instructions);

  if (data == nullptr) {
    abort();
  }

  MetadataLock lock;

  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) {
      return -1;
    }

    // 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));
    gNumCodeObjects = new_size;
  }

  DCHECK(gCodeObjects[i].code_info == nullptr);

  // Find out where the next entry should go.
  if (gCodeObjects[i].next_free == 0) {
    // if this is a fresh entry, use the next one.
    gNextCodeObject = i + 1;
    DCHECK(gNextCodeObject == gNumCodeObjects ||
           (gCodeObjects[gNextCodeObject].code_info == nullptr &&
            gCodeObjects[gNextCodeObject].next_free == 0));
  } else {
    gNextCodeObject = gCodeObjects[i].next_free - 1;
  }

  if (i <= int_max) {
    gCodeObjects[i].code_info = data;
    return static_cast<int>(i);
  } else {
    return -1;
  }
}

void ReleaseHandlerData(int index) {
  // 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;

    // +1 because we reserve {next_entry == 0} to indicate a fresh list entry.
    gCodeObjects[index].next_free = gNextCodeObject + 1;
    gNextCodeObject = index;
  }
  // TODO(eholk): on debug builds, ensure there are no more copies in
  // the list.
  free(data);
}

bool RegisterDefaultSignalHandler() {
#if V8_TRAP_HANDLER_SUPPORTED
  struct sigaction action;
  action.sa_sigaction = HandleSignal;
  action.sa_flags = SA_SIGINFO;
  sigemptyset(&action.sa_mask);
  // {sigaction} installs a new custom segfault handler. On success, it returns
  // 0. If we get a nonzero value, we report an error to the caller by returning
  // false.
  if (sigaction(SIGSEGV, &action, nullptr) != 0) {
    return false;
  }

  return true;
#else
  return false;
#endif
}

}  // namespace trap_handler
}  // namespace internal
}  // namespace v8