LCOV - code coverage report
Current view: top level - src/trap-handler - handler-inside.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 24 32 75.0 %
Date: 2017-04-26 Functions: 2 2 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 out of bounds signal handler for
       8             : // WebAssembly. Signal handlers are notoriously difficult to get
       9             : // right, and getting it wrong can lead to security
      10             : // vulnerabilities. In order to minimize this risk, here are some
      11             : // rules to follow.
      12             : //
      13             : // 1. Do not introduce any new external dependencies. This file needs
      14             : //    to be self contained so it is easy to audit everything that a
      15             : //    signal handler might do.
      16             : //
      17             : // 2. Any changes must be reviewed by someone from the crash reporting
      18             : //    or security team. See OWNERS for suggested reviewers.
      19             : //
      20             : // For more information, see https://goo.gl/yMeyUY.
      21             : //
      22             : // This file contains most of the code that actually runs in a signal handler
      23             : // context. Some additional code is used both inside and outside the signal
      24             : // handler. This code can be found in handler-shared.cc.
      25             : 
      26             : #include <signal.h>
      27             : #include <stddef.h>
      28             : #include <stdlib.h>
      29             : 
      30             : #include "src/trap-handler/trap-handler-internal.h"
      31             : #include "src/trap-handler/trap-handler.h"
      32             : 
      33             : namespace v8 {
      34             : namespace internal {
      35             : namespace trap_handler {
      36             : 
      37             : namespace {
      38             : 
      39             : bool IsKernelGeneratedSignal(siginfo_t* info) {
      40             :   return info->si_code > 0 && info->si_code != SI_USER &&
      41             :          info->si_code != SI_QUEUE && info->si_code != SI_TIMER &&
      42             :          info->si_code != SI_ASYNCIO && info->si_code != SI_MESGQ;
      43             : }
      44             : 
      45             : #if V8_TRAP_HANDLER_SUPPORTED
      46             : class SigUnmaskStack {
      47             :  public:
      48             :   explicit SigUnmaskStack(sigset_t sigs) {
      49             :     // TODO(eholk): consider using linux-syscall-support for calling this
      50             :     // syscall.
      51        2190 :     pthread_sigmask(SIG_UNBLOCK, &sigs, &old_mask_);
      52             :   }
      53             : 
      54        2190 :   ~SigUnmaskStack() { pthread_sigmask(SIG_SETMASK, &old_mask_, nullptr); }
      55             : 
      56             :  private:
      57             :   sigset_t old_mask_;
      58             : 
      59             :   // We'd normally use DISALLOW_COPY_AND_ASSIGN, but we're avoiding a dependency
      60             :   // on base/macros.h
      61             :   SigUnmaskStack(const SigUnmaskStack&) = delete;
      62             :   void operator=(const SigUnmaskStack&) = delete;
      63             : };
      64             : #endif
      65             : }  // namespace
      66             : 
      67             : #if V8_TRAP_HANDLER_SUPPORTED && V8_OS_LINUX
      68        4380 : bool TryHandleSignal(int signum, siginfo_t* info, ucontext_t* context) {
      69             :   // Bail out early in case we got called for the wrong kind of signal.
      70        2190 :   if (signum != SIGSEGV) {
      71             :     return false;
      72             :   }
      73             : 
      74             :   // Make sure the signal was generated by the kernel and not some other source.
      75        2190 :   if (!IsKernelGeneratedSignal(info)) {
      76             :     return false;
      77             :   }
      78             : 
      79             :   // Ensure the faulting thread was actually running Wasm code.
      80        2190 :   if (!IsThreadInWasm()) {
      81             :     return false;
      82             :   }
      83             : 
      84             :   // Clear g_thread_in_wasm_code, primarily to protect against nested faults.
      85        2190 :   g_thread_in_wasm_code = false;
      86             : 
      87             :   // Begin signal mask scope. We need to be sure to restore the signal mask
      88             :   // before we restore the g_thread_in_wasm_code flag.
      89             :   {
      90             :     // Unmask the signal so that if this signal handler crashes, the crash will
      91             :     // be handled by the crash reporter.  Otherwise, the process might be killed
      92             :     // with the crash going unreported.
      93             :     sigset_t sigs;
      94             :     // Fortunately, sigemptyset and sigaddset are async-signal-safe according to
      95             :     // the POSIX standard.
      96        2190 :     sigemptyset(&sigs);
      97        2190 :     sigaddset(&sigs, SIGSEGV);
      98             :     SigUnmaskStack unmask(sigs);
      99             : 
     100        2190 :     uintptr_t fault_addr = context->uc_mcontext.gregs[REG_RIP];
     101             : 
     102             :     // TODO(eholk): broad code range check
     103             : 
     104             :     // Taking locks in a signal handler is risky because a fault in the signal
     105             :     // handler could lead to a deadlock when attempting to acquire the lock
     106             :     // again. We guard against this case with g_thread_in_wasm_code. The lock
     107             :     // may only be taken when not executing Wasm code (an assert in
     108             :     // MetadataLock's constructor ensures this). This signal handler will bail
     109             :     // out before trying to take the lock if g_thread_in_wasm_code is not set.
     110        2190 :     MetadataLock lock_holder;
     111             : 
     112        8956 :     for (size_t i = 0; i < gNumCodeObjects; ++i) {
     113        8956 :       const CodeProtectionInfo* data = gCodeObjects[i].code_info;
     114        8956 :       if (data == nullptr) {
     115             :         continue;
     116             :       }
     117        5256 :       const uintptr_t base = reinterpret_cast<uintptr_t>(data->base);
     118             : 
     119        5256 :       if (fault_addr >= base && fault_addr < base + data->size) {
     120             :         // Hurray, we found the code object. Check for protected addresses.
     121        2190 :         const ptrdiff_t offset = fault_addr - base;
     122             : 
     123        5205 :         for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
     124        5205 :           if (data->instructions[i].instr_offset == offset) {
     125             :             // Hurray again, we found the actual instruction. Tell the caller to
     126             :             // return to the landing pad.
     127             :             context->uc_mcontext.gregs[REG_RIP] =
     128        2190 :                 data->instructions[i].landing_offset + base;
     129        2190 :             return true;
     130             :           }
     131             :         }
     132             :       }
     133             :     }
     134             :   }  // end signal mask scope
     135             : 
     136             :   // If we get here, it's not a recoverable wasm fault, so we go to the next
     137             :   // handler.
     138           0 :   g_thread_in_wasm_code = true;
     139           0 :   return false;
     140             : }
     141             : #endif  // V8_TRAP_HANDLER_SUPPORTED && V8_OS_LINUX
     142             : 
     143             : #if V8_TRAP_HANDLER_SUPPORTED
     144        2190 : void HandleSignal(int signum, siginfo_t* info, void* context) {
     145             :   ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
     146             : 
     147        2190 :   if (!TryHandleSignal(signum, info, uc)) {
     148             :     // Since V8 didn't handle this signal, we want to re-raise the same signal.
     149             :     // For kernel-generated SEGV signals, we do this by restoring the default
     150             :     // SEGV handler and then returning. The fault will happen again and the
     151             :     // usual SEGV handling will happen.
     152             :     //
     153             :     // We handle user-generated signals by calling raise() instead. This is for
     154             :     // completeness. We should never actually see one of these, but just in
     155             :     // case, we do the right thing.
     156             :     struct sigaction action;
     157           0 :     action.sa_handler = SIG_DFL;
     158           0 :     sigemptyset(&action.sa_mask);
     159           0 :     action.sa_flags = 0;
     160           0 :     sigaction(signum, &action, nullptr);
     161           0 :     if (!IsKernelGeneratedSignal(info)) {
     162           0 :       raise(signum);
     163             :     }
     164             :   }
     165             :   // TryHandleSignal modifies context to change where we return to.
     166        2190 : }
     167             : #endif
     168             : }  // namespace trap_handler
     169             : }  // namespace internal
     170             : }  // namespace v8

Generated by: LCOV version 1.10