LCOV - code coverage report
Current view: top level - src/trap-handler - handler-inside.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 33 35 94.3 %
Date: 2017-10-20 Functions: 3 3 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        2433 :     pthread_sigmask(SIG_UNBLOCK, &sigs, &old_mask_);
      52             :   }
      53             : 
      54        2433 :   ~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        4868 : 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        2434 :   if (signum != SIGSEGV) {
      71             :     return false;
      72             :   }
      73             : 
      74             :   // Make sure the signal was generated by the kernel and not some other source.
      75        2434 :   if (!IsKernelGeneratedSignal(info)) {
      76             :     return false;
      77             :   }
      78             : 
      79             :   // Ensure the faulting thread was actually running Wasm code.
      80        2434 :   if (!IsThreadInWasm()) {
      81             :     return false;
      82             :   }
      83             : 
      84             :   // Clear g_thread_in_wasm_code, primarily to protect against nested faults.
      85        2433 :   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        2433 :     sigemptyset(&sigs);
      97        2433 :     sigaddset(&sigs, SIGSEGV);
      98             :     SigUnmaskStack unmask(sigs);
      99             : 
     100        2433 :     uintptr_t fault_addr = context->uc_mcontext.gregs[REG_RIP];
     101        2433 :     uintptr_t landing_pad = 0;
     102        2433 :     if (TryFindLandingPad(fault_addr, &landing_pad)) {
     103             :       // Tell the caller to return to the landing pad.
     104        2433 :       context->uc_mcontext.gregs[REG_RIP] = landing_pad;
     105             :       // We will return to wasm code, so restore the g_thread_in_wasm_code flag.
     106        2433 :       g_thread_in_wasm_code = true;
     107             :       return true;
     108             :     }
     109             :   }  // end signal mask scope
     110             : 
     111             :   // If we get here, it's not a recoverable wasm fault, so we go to the next
     112             :   // handler. Leave the g_thread_in_wasm_code flag unset since we do not return
     113             :   // to wasm code.
     114           0 :   return false;
     115             : }
     116             : 
     117             : // This function contains the platform independent portions of fault
     118             : // classification.
     119        2433 : bool TryFindLandingPad(uintptr_t fault_addr, uintptr_t* landing_pad) {
     120             :   // TODO(eholk): broad code range check
     121             : 
     122             :   // Taking locks in a signal handler is risky because a fault in the signal
     123             :   // handler could lead to a deadlock when attempting to acquire the lock
     124             :   // again. We guard against this case with g_thread_in_wasm_code. The lock
     125             :   // may only be taken when not executing Wasm code (an assert in
     126             :   // MetadataLock's constructor ensures this). This signal handler will bail
     127             :   // out before trying to take the lock if g_thread_in_wasm_code is not set.
     128        2433 :   MetadataLock lock_holder;
     129             : 
     130       13800 :   for (size_t i = 0; i < gNumCodeObjects; ++i) {
     131       13800 :     const CodeProtectionInfo* data = gCodeObjects[i].code_info;
     132       13800 :     if (data == nullptr) {
     133             :       continue;
     134             :     }
     135        8632 :     const uintptr_t base = reinterpret_cast<uintptr_t>(data->base);
     136             : 
     137        8632 :     if (fault_addr >= base && fault_addr < base + data->size) {
     138             :       // Hurray, we found the code object. Check for protected addresses.
     139        2433 :       const ptrdiff_t offset = fault_addr - base;
     140             : 
     141        5453 :       for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
     142        5453 :         if (data->instructions[i].instr_offset == offset) {
     143             :           // Hurray again, we found the actual instruction.
     144        2433 :           *landing_pad = data->instructions[i].landing_offset + base;
     145             : 
     146             :           gRecoveredTrapCount.store(
     147             :               gRecoveredTrapCount.load(std::memory_order_relaxed) + 1,
     148        2433 :               std::memory_order_relaxed);
     149             : 
     150        2433 :           return true;
     151             :         }
     152             :       }
     153             :     }
     154             :   }
     155        2433 :   return false;
     156             : }
     157             : #endif  // V8_TRAP_HANDLER_SUPPORTED && V8_OS_LINUX
     158             : 
     159             : #if V8_TRAP_HANDLER_SUPPORTED
     160        2435 : void HandleSignal(int signum, siginfo_t* info, void* context) {
     161             :   ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
     162             : 
     163        2434 :   if (!TryHandleSignal(signum, info, uc)) {
     164             :     // Since V8 didn't handle this signal, we want to re-raise the same signal.
     165             :     // For kernel-generated SEGV signals, we do this by restoring the original
     166             :     // SEGV handler and then returning. The fault will happen again and the
     167             :     // usual SEGV handling will happen.
     168             :     //
     169             :     // We handle user-generated signals by calling raise() instead. This is for
     170             :     // completeness. We should never actually see one of these, but just in
     171             :     // case, we do the right thing.
     172           1 :     RestoreOriginalSignalHandler();
     173           1 :     if (!IsKernelGeneratedSignal(info)) {
     174           0 :       raise(signum);
     175             :     }
     176             :   }
     177             :   // TryHandleSignal modifies context to change where we return to.
     178        2434 : }
     179             : #endif
     180             : }  // namespace trap_handler
     181             : }  // namespace internal
     182             : }  // namespace v8

Generated by: LCOV version 1.10