• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 "src/trap-handler/handler-inside-posix.h"
27 
28 #include <signal.h>
29 
30 #if defined(V8_OS_LINUX) || defined(V8_OS_FREEBSD)
31 #include <ucontext.h>
32 #elif V8_OS_MACOSX
33 #include <sys/ucontext.h>
34 #endif
35 
36 #include <stddef.h>
37 #include <stdlib.h>
38 
39 #include "src/trap-handler/trap-handler-internal.h"
40 #include "src/trap-handler/trap-handler.h"
41 
42 namespace v8 {
43 namespace internal {
44 namespace trap_handler {
45 
IsKernelGeneratedSignal(siginfo_t * info)46 bool IsKernelGeneratedSignal(siginfo_t* info) {
47   // On macOS, only `info->si_code > 0` is relevant, because macOS leaves
48   // si_code at its default of 0 for signals that don’t originate in hardware.
49   // The other conditions are only relevant for Linux.
50   return info->si_code > 0 && info->si_code != SI_USER &&
51          info->si_code != SI_QUEUE && info->si_code != SI_TIMER &&
52          info->si_code != SI_ASYNCIO && info->si_code != SI_MESGQ;
53 }
54 
55 class SigUnmaskStack {
56  public:
SigUnmaskStack(sigset_t sigs)57   explicit SigUnmaskStack(sigset_t sigs) {
58     // TODO(eholk): consider using linux-syscall-support for calling this
59     // syscall.
60     pthread_sigmask(SIG_UNBLOCK, &sigs, &old_mask_);
61   }
62 
63   // We'd normally use DISALLOW_COPY_AND_ASSIGN, but we're avoiding a dependency
64   // on base/macros.h
65   SigUnmaskStack(const SigUnmaskStack&) = delete;
66   void operator=(const SigUnmaskStack&) = delete;
67 
~SigUnmaskStack()68   ~SigUnmaskStack() { pthread_sigmask(SIG_SETMASK, &old_mask_, nullptr); }
69 
70  private:
71   sigset_t old_mask_;
72 };
73 
TryHandleSignal(int signum,siginfo_t * info,void * context)74 bool TryHandleSignal(int signum, siginfo_t* info, void* context) {
75   // Ensure the faulting thread was actually running Wasm code. This should be
76   // the first check in the trap handler to guarantee that the IsThreadInWasm
77   // flag is only set in wasm code. Otherwise a later signal handler is executed
78   // with the flag set.
79   if (!IsThreadInWasm()) {
80     return false;
81   }
82 
83   // Clear g_thread_in_wasm_code, primarily to protect against nested faults.
84   g_thread_in_wasm_code = false;
85 
86   // Bail out early in case we got called for the wrong kind of signal.
87 
88   if (signum != kOobSignal) {
89     return false;
90   }
91 
92   // Make sure the signal was generated by the kernel and not some other source.
93   if (!IsKernelGeneratedSignal(info)) {
94     return false;
95   }
96 
97   // Begin signal mask scope. We need to be sure to restore the signal mask
98   // before we restore the g_thread_in_wasm_code flag.
99   {
100     // Unmask the signal so that if this signal handler crashes, the crash will
101     // be handled by the crash reporter.  Otherwise, the process might be killed
102     // with the crash going unreported.
103     sigset_t sigs;
104     // Fortunately, sigemptyset and sigaddset are async-signal-safe according to
105     // the POSIX standard.
106     sigemptyset(&sigs);
107     sigaddset(&sigs, SIGSEGV);
108     SigUnmaskStack unmask(sigs);
109 
110     ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
111 #if V8_OS_LINUX
112     auto* context_rip = &uc->uc_mcontext.gregs[REG_RIP];
113 #elif V8_OS_MACOSX
114     auto* context_rip = &uc->uc_mcontext->__ss.__rip;
115 #elif V8_OS_FREEBSD
116     auto* context_rip = &uc->uc_mcontext.mc_rip;
117 #else
118 #error Unsupported platform
119 #endif
120     uintptr_t fault_addr = *context_rip;
121     uintptr_t landing_pad = 0;
122     if (TryFindLandingPad(fault_addr, &landing_pad)) {
123       // Tell the caller to return to the landing pad.
124       *context_rip = landing_pad;
125       // We will return to wasm code, so restore the g_thread_in_wasm_code flag.
126       g_thread_in_wasm_code = true;
127       return true;
128     }
129   }  // end signal mask scope
130 
131   // If we get here, it's not a recoverable wasm fault, so we go to the next
132   // handler. Leave the g_thread_in_wasm_code flag unset since we do not return
133   // to wasm code.
134   return false;
135 }
136 
HandleSignal(int signum,siginfo_t * info,void * context)137 void HandleSignal(int signum, siginfo_t* info, void* context) {
138   if (!TryHandleSignal(signum, info, context)) {
139     // Since V8 didn't handle this signal, we want to re-raise the same signal.
140     // For kernel-generated SEGV signals, we do this by restoring the original
141     // SEGV handler and then returning. The fault will happen again and the
142     // usual SEGV handling will happen.
143     //
144     // We handle user-generated signals by calling raise() instead. This is for
145     // completeness. We should never actually see one of these, but just in
146     // case, we do the right thing.
147     RemoveTrapHandler();
148     if (!IsKernelGeneratedSignal(info)) {
149       raise(signum);
150     }
151   }
152   // TryHandleSignal modifies context to change where we return to.
153 }
154 
155 }  // namespace trap_handler
156 }  // namespace internal
157 }  // namespace v8
158