handler-inside-posix.cc 5.26 KB
Newer Older
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
// Copyright 2018 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 out of bounds signal handler for
// WebAssembly. Signal handlers are notoriously difficult to get
// right, and getting it wrong can lead to security
// vulnerabilities. In order to minimize this risk, here are some
// rules to follow.
//
// 1. Do not introduce any new external dependencies. This file needs
//    to be self contained so it is easy to audit everything that a
//    signal handler might do.
//
// 2. Any changes must be reviewed by someone from the crash reporting
//    or security team. See OWNERS for suggested reviewers.
//
// For more information, see https://goo.gl/yMeyUY.
//
// This file contains most of the code that actually runs in a signal handler
// context. Some additional code is used both inside and outside the signal
// handler. This code can be found in handler-shared.cc.

26 27
#include "src/trap-handler/handler-inside-posix.h"

28
#include <signal.h>
29 30 31 32 33 34 35

#ifdef V8_OS_LINUX
#include <ucontext.h>
#elif V8_OS_MACOSX
#include <sys/ucontext.h>
#endif

36 37 38 39 40 41 42 43 44 45 46
#include <stddef.h>
#include <stdlib.h>

#include "src/trap-handler/trap-handler-internal.h"
#include "src/trap-handler/trap-handler.h"

namespace v8 {
namespace internal {
namespace trap_handler {

bool IsKernelGeneratedSignal(siginfo_t* info) {
47 48 49
  // On macOS, only `info->si_code > 0` is relevant, because macOS leaves
  // si_code at its default of 0 for signals that don’t originate in hardware.
  // The other conditions are only relevant for Linux.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
  return info->si_code > 0 && info->si_code != SI_USER &&
         info->si_code != SI_QUEUE && info->si_code != SI_TIMER &&
         info->si_code != SI_ASYNCIO && info->si_code != SI_MESGQ;
}

class SigUnmaskStack {
 public:
  explicit SigUnmaskStack(sigset_t sigs) {
    // TODO(eholk): consider using linux-syscall-support for calling this
    // syscall.
    pthread_sigmask(SIG_UNBLOCK, &sigs, &old_mask_);
  }

  ~SigUnmaskStack() { pthread_sigmask(SIG_SETMASK, &old_mask_, nullptr); }

 private:
  sigset_t old_mask_;

  // We'd normally use DISALLOW_COPY_AND_ASSIGN, but we're avoiding a dependency
  // on base/macros.h
  SigUnmaskStack(const SigUnmaskStack&) = delete;
  void operator=(const SigUnmaskStack&) = delete;
};

74
bool TryHandleSignal(int signum, siginfo_t* info, void* context) {
75 76 77 78 79 80 81 82 83 84 85
  // Ensure the faulting thread was actually running Wasm code. This should be
  // the first check in the trap handler to guarantee that the IsThreadInWasm
  // flag is only set in wasm code. Otherwise a later signal handler is executed
  // with the flag set.
  if (!IsThreadInWasm()) {
    return false;
  }

  // Clear g_thread_in_wasm_code, primarily to protect against nested faults.
  g_thread_in_wasm_code = false;

86
  // Bail out early in case we got called for the wrong kind of signal.
87 88

  if (signum != kOobSignal) {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    return false;
  }

  // Make sure the signal was generated by the kernel and not some other source.
  if (!IsKernelGeneratedSignal(info)) {
    return false;
  }

  // Begin signal mask scope. We need to be sure to restore the signal mask
  // before we restore the g_thread_in_wasm_code flag.
  {
    // Unmask the signal so that if this signal handler crashes, the crash will
    // be handled by the crash reporter.  Otherwise, the process might be killed
    // with the crash going unreported.
    sigset_t sigs;
    // Fortunately, sigemptyset and sigaddset are async-signal-safe according to
    // the POSIX standard.
    sigemptyset(&sigs);
    sigaddset(&sigs, SIGSEGV);
    SigUnmaskStack unmask(sigs);

110
    ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
111 112 113 114 115 116 117 118
#if V8_OS_LINUX
    auto* context_rip = &uc->uc_mcontext.gregs[REG_RIP];
#elif V8_OS_MACOSX
    auto* context_rip = &uc->uc_mcontext->__ss.__rip;
#else
#error Unsupported platform
#endif
    uintptr_t fault_addr = *context_rip;
119 120 121
    uintptr_t landing_pad = 0;
    if (TryFindLandingPad(fault_addr, &landing_pad)) {
      // Tell the caller to return to the landing pad.
122
      *context_rip = landing_pad;
123 124 125 126 127 128 129 130 131 132 133 134 135
      // We will return to wasm code, so restore the g_thread_in_wasm_code flag.
      g_thread_in_wasm_code = true;
      return true;
    }
  }  // end signal mask scope

  // If we get here, it's not a recoverable wasm fault, so we go to the next
  // handler. Leave the g_thread_in_wasm_code flag unset since we do not return
  // to wasm code.
  return false;
}

void HandleSignal(int signum, siginfo_t* info, void* context) {
136
  if (!TryHandleSignal(signum, info, context)) {
137 138 139 140 141 142 143 144
    // Since V8 didn't handle this signal, we want to re-raise the same signal.
    // For kernel-generated SEGV signals, we do this by restoring the original
    // SEGV handler and then returning. The fault will happen again and the
    // usual SEGV handling will happen.
    //
    // We handle user-generated signals by calling raise() instead. This is for
    // completeness. We should never actually see one of these, but just in
    // case, we do the right thing.
145
    RemoveTrapHandler();
146 147 148 149 150 151 152 153 154 155
    if (!IsKernelGeneratedSignal(info)) {
      raise(signum);
    }
  }
  // TryHandleSignal modifies context to change where we return to.
}

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