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 trap handler for 8// WebAssembly. Exception 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// trap 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 an exception 23// handler context. Some additional code is used both inside and outside the 24// trap handler. This code can be found in handler-shared.cc. 25 26#include "src/trap-handler/handler-inside-win.h" 27 28#include <windows.h> 29 30#include "src/trap-handler/trap-handler-internal.h" 31#include "src/trap-handler/trap-handler.h" 32 33#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR 34#include "src/trap-handler/trap-handler-simulator.h" 35#endif 36 37namespace v8 { 38namespace internal { 39namespace trap_handler { 40 41// The below struct needed to access the offset in the Thread Environment Block 42// to see if the thread local storage for the thread has been allocated yet. 43// 44// The ThreadLocalStorage pointer is located 12 pointers into the TEB (i.e. at 45// offset 0x58 for 64-bit platforms, and 0x2c for 32-bit platforms). This is 46// true for x64, x86, ARM, and ARM64 platforms (see the header files in the SDK 47// named ksamd64.inc, ks386.inc, ksarm.h, and ksarm64.h respectively). 48// 49// These offsets are baked into compiled binaries, so can never be changed for 50// backwards compatibility reasons. 51struct TEB { 52 PVOID reserved[11]; 53 PVOID thread_local_storage_pointer; 54}; 55 56#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR 57// This is the address where we continue on a failed "ProbeMemory". It's defined 58// in "handler-outside-simulator.cc". 59extern "C" char v8_probe_memory_continuation[]; 60#endif // V8_TRAP_HANDLER_VIA_SIMULATOR 61 62bool TryHandleWasmTrap(EXCEPTION_POINTERS* exception) { 63 // VectoredExceptionHandlers need extreme caution. Do as little as possible 64 // to determine if the exception should be handled or not. Exceptions can be 65 // thrown very early in a threads life, before the thread has even completed 66 // initializing. As a demonstrative example, there was a bug (#8966) where an 67 // exception would be raised before the thread local copy of the 68 // "__declspec(thread)" variables had been allocated, the handler tried to 69 // access the thread-local "g_thread_in_wasm_code", which would then raise 70 // another exception, and an infinite loop ensued. 71 72 // First ensure this is an exception type of interest 73 if (exception->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { 74 return false; 75 } 76 77 // See if thread-local storage for __declspec(thread) variables has been 78 // allocated yet. This pointer is initially null in the TEB until the 79 // loader has completed allocating the memory for thread_local variables 80 // and copy constructed their initial values. (Note: Any functions that 81 // need to run to initialize values may not have run yet, but that is not 82 // the case for any thread_locals used here). 83 TEB* pteb = reinterpret_cast<TEB*>(NtCurrentTeb()); 84 if (!pteb->thread_local_storage_pointer) return false; 85 86 // Now safe to run more advanced logic, which may access thread_locals 87 // Ensure the faulting thread was actually running Wasm code. 88 if (!IsThreadInWasm()) return false; 89 90 // Clear g_thread_in_wasm_code, primarily to protect against nested faults. 91 // The only path that resets the flag to true is if we find a landing pad (in 92 // which case this function returns true). Otherwise we leave the flag unset 93 // since we do not return to wasm code. 94 g_thread_in_wasm_code = false; 95 96 const EXCEPTION_RECORD* record = exception->ExceptionRecord; 97 98 uintptr_t fault_addr = reinterpret_cast<uintptr_t>(record->ExceptionAddress); 99 uintptr_t landing_pad = 0; 100 101#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR 102 // Only handle signals triggered by the load in {ProbeMemory}. 103 if (fault_addr != reinterpret_cast<uintptr_t>(&ProbeMemory)) return false; 104 105 // The simulated ip will be in the second parameter register (%rdx). 106 uintptr_t simulated_ip = exception->ContextRecord->Rdx; 107 if (!TryFindLandingPad(simulated_ip, &landing_pad)) return false; 108 TH_DCHECK(landing_pad != 0); 109 110 exception->ContextRecord->Rax = landing_pad; 111 // Continue at the memory probing continuation. 112 exception->ContextRecord->Rip = 113 reinterpret_cast<uintptr_t>(&v8_probe_memory_continuation); 114#else 115 if (!TryFindLandingPad(fault_addr, &landing_pad)) return false; 116 117 // Tell the caller to return to the landing pad. 118 exception->ContextRecord->Rip = landing_pad; 119#endif 120 // We will return to wasm code, so restore the g_thread_in_wasm_code flag. 121 g_thread_in_wasm_code = true; 122 return true; 123} 124 125LONG HandleWasmTrap(EXCEPTION_POINTERS* exception) { 126 if (TryHandleWasmTrap(exception)) { 127 return EXCEPTION_CONTINUE_EXECUTION; 128 } 129 return EXCEPTION_CONTINUE_SEARCH; 130} 131 132} // namespace trap_handler 133} // namespace internal 134} // namespace v8 135