1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/AsmJSSignalHandlers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1043 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/AsmJSSignalHandlers.h" 1.11 + 1.12 +#include "mozilla/BinarySearch.h" 1.13 + 1.14 +#include "assembler/assembler/MacroAssembler.h" 1.15 +#include "jit/AsmJSModule.h" 1.16 + 1.17 +using namespace js; 1.18 +using namespace js::jit; 1.19 +using namespace mozilla; 1.20 + 1.21 +using JS::GenericNaN; 1.22 + 1.23 +#if defined(XP_WIN) 1.24 +# define XMM_sig(p,i) ((p)->Xmm##i) 1.25 +# define EIP_sig(p) ((p)->Eip) 1.26 +# define RIP_sig(p) ((p)->Rip) 1.27 +# define RAX_sig(p) ((p)->Rax) 1.28 +# define RCX_sig(p) ((p)->Rcx) 1.29 +# define RDX_sig(p) ((p)->Rdx) 1.30 +# define RBX_sig(p) ((p)->Rbx) 1.31 +# define RSP_sig(p) ((p)->Rsp) 1.32 +# define RBP_sig(p) ((p)->Rbp) 1.33 +# define RSI_sig(p) ((p)->Rsi) 1.34 +# define RDI_sig(p) ((p)->Rdi) 1.35 +# define R8_sig(p) ((p)->R8) 1.36 +# define R9_sig(p) ((p)->R9) 1.37 +# define R10_sig(p) ((p)->R10) 1.38 +# define R11_sig(p) ((p)->R11) 1.39 +# define R12_sig(p) ((p)->R12) 1.40 +# define R13_sig(p) ((p)->R13) 1.41 +# define R14_sig(p) ((p)->R14) 1.42 +# define R15_sig(p) ((p)->R15) 1.43 +#elif defined(__OpenBSD__) 1.44 +# define XMM_sig(p,i) ((p)->sc_fpstate->fx_xmm[i]) 1.45 +# define EIP_sig(p) ((p)->sc_eip) 1.46 +# define RIP_sig(p) ((p)->sc_rip) 1.47 +# define RAX_sig(p) ((p)->sc_rax) 1.48 +# define RCX_sig(p) ((p)->sc_rcx) 1.49 +# define RDX_sig(p) ((p)->sc_rdx) 1.50 +# define RBX_sig(p) ((p)->sc_rbx) 1.51 +# define RSP_sig(p) ((p)->sc_rsp) 1.52 +# define RBP_sig(p) ((p)->sc_rbp) 1.53 +# define RSI_sig(p) ((p)->sc_rsi) 1.54 +# define RDI_sig(p) ((p)->sc_rdi) 1.55 +# define R8_sig(p) ((p)->sc_r8) 1.56 +# define R9_sig(p) ((p)->sc_r9) 1.57 +# define R10_sig(p) ((p)->sc_r10) 1.58 +# define R11_sig(p) ((p)->sc_r11) 1.59 +# define R12_sig(p) ((p)->sc_r12) 1.60 +# define R13_sig(p) ((p)->sc_r13) 1.61 +# define R14_sig(p) ((p)->sc_r14) 1.62 +# define R15_sig(p) ((p)->sc_r15) 1.63 +#elif defined(__linux__) || defined(SOLARIS) 1.64 +# if defined(__linux__) 1.65 +# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i]) 1.66 +# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP]) 1.67 +# else 1.68 +# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i]) 1.69 +# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC]) 1.70 +# endif 1.71 +# define RIP_sig(p) ((p)->uc_mcontext.gregs[REG_RIP]) 1.72 +# define RAX_sig(p) ((p)->uc_mcontext.gregs[REG_RAX]) 1.73 +# define RCX_sig(p) ((p)->uc_mcontext.gregs[REG_RCX]) 1.74 +# define RDX_sig(p) ((p)->uc_mcontext.gregs[REG_RDX]) 1.75 +# define RBX_sig(p) ((p)->uc_mcontext.gregs[REG_RBX]) 1.76 +# define RSP_sig(p) ((p)->uc_mcontext.gregs[REG_RSP]) 1.77 +# define RBP_sig(p) ((p)->uc_mcontext.gregs[REG_RBP]) 1.78 +# define RSI_sig(p) ((p)->uc_mcontext.gregs[REG_RSI]) 1.79 +# define RDI_sig(p) ((p)->uc_mcontext.gregs[REG_RDI]) 1.80 +# define R8_sig(p) ((p)->uc_mcontext.gregs[REG_R8]) 1.81 +# define R9_sig(p) ((p)->uc_mcontext.gregs[REG_R9]) 1.82 +# define R10_sig(p) ((p)->uc_mcontext.gregs[REG_R10]) 1.83 +# define R11_sig(p) ((p)->uc_mcontext.gregs[REG_R11]) 1.84 +# define R12_sig(p) ((p)->uc_mcontext.gregs[REG_R12]) 1.85 +# define R13_sig(p) ((p)->uc_mcontext.gregs[REG_R13]) 1.86 +# define R14_sig(p) ((p)->uc_mcontext.gregs[REG_R14]) 1.87 +# if defined(__linux__) && defined(__arm__) 1.88 +# define R15_sig(p) ((p)->uc_mcontext.arm_pc) 1.89 +# else 1.90 +# define R15_sig(p) ((p)->uc_mcontext.gregs[REG_R15]) 1.91 +# endif 1.92 +#elif defined(__NetBSD__) 1.93 +# define XMM_sig(p,i) (((struct fxsave64 *)(p)->uc_mcontext.__fpregs)->fx_xmm[i]) 1.94 +# define EIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EIP]) 1.95 +# define RIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RIP]) 1.96 +# define RAX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RAX]) 1.97 +# define RCX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RCX]) 1.98 +# define RDX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RDX]) 1.99 +# define RBX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBX]) 1.100 +# define RSP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSP]) 1.101 +# define RBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBP]) 1.102 +# define RSI_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSI]) 1.103 +# define RDI_sig(p) ((p)->uc_mcontext.__gregs[_REG_RDI]) 1.104 +# define R8_sig(p) ((p)->uc_mcontext.__gregs[_REG_R8]) 1.105 +# define R9_sig(p) ((p)->uc_mcontext.__gregs[_REG_R9]) 1.106 +# define R10_sig(p) ((p)->uc_mcontext.__gregs[_REG_R10]) 1.107 +# define R11_sig(p) ((p)->uc_mcontext.__gregs[_REG_R11]) 1.108 +# define R12_sig(p) ((p)->uc_mcontext.__gregs[_REG_R12]) 1.109 +# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13]) 1.110 +# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14]) 1.111 +# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15]) 1.112 +#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 1.113 +# if defined(__DragonFly__) 1.114 +# define XMM_sig(p,i) (((union savefpu *)(p)->uc_mcontext.mc_fpregs)->sv_xmm.sv_xmm[i]) 1.115 +# else 1.116 +# define XMM_sig(p,i) (((struct savefpu *)(p)->uc_mcontext.mc_fpstate)->sv_xmm[i]) 1.117 +# endif 1.118 +# define EIP_sig(p) ((p)->uc_mcontext.mc_eip) 1.119 +# define RIP_sig(p) ((p)->uc_mcontext.mc_rip) 1.120 +# define RAX_sig(p) ((p)->uc_mcontext.mc_rax) 1.121 +# define RCX_sig(p) ((p)->uc_mcontext.mc_rcx) 1.122 +# define RDX_sig(p) ((p)->uc_mcontext.mc_rdx) 1.123 +# define RBX_sig(p) ((p)->uc_mcontext.mc_rbx) 1.124 +# define RSP_sig(p) ((p)->uc_mcontext.mc_rsp) 1.125 +# define RBP_sig(p) ((p)->uc_mcontext.mc_rbp) 1.126 +# define RSI_sig(p) ((p)->uc_mcontext.mc_rsi) 1.127 +# define RDI_sig(p) ((p)->uc_mcontext.mc_rdi) 1.128 +# define R8_sig(p) ((p)->uc_mcontext.mc_r8) 1.129 +# define R9_sig(p) ((p)->uc_mcontext.mc_r9) 1.130 +# define R10_sig(p) ((p)->uc_mcontext.mc_r10) 1.131 +# define R11_sig(p) ((p)->uc_mcontext.mc_r11) 1.132 +# define R12_sig(p) ((p)->uc_mcontext.mc_r12) 1.133 +# define R13_sig(p) ((p)->uc_mcontext.mc_r13) 1.134 +# define R14_sig(p) ((p)->uc_mcontext.mc_r14) 1.135 +# if defined(__FreeBSD__) && defined(__arm__) 1.136 +# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15]) 1.137 +# else 1.138 +# define R15_sig(p) ((p)->uc_mcontext.mc_r15) 1.139 +# endif 1.140 +#elif defined(XP_MACOSX) 1.141 +// Mach requires special treatment. 1.142 +#else 1.143 +# error "Don't know how to read/write to the thread state via the mcontext_t." 1.144 +#endif 1.145 + 1.146 +// For platforms where the signal/exception handler runs on the same 1.147 +// thread/stack as the victim (Unix and Windows), we can use TLS to find any 1.148 +// currently executing asm.js code. 1.149 +#if !defined(XP_MACOSX) 1.150 +static AsmJSActivation * 1.151 +InnermostAsmJSActivation() 1.152 +{ 1.153 + PerThreadData *threadData = TlsPerThreadData.get(); 1.154 + if (!threadData) 1.155 + return nullptr; 1.156 + 1.157 + return threadData->asmJSActivationStackFromOwnerThread(); 1.158 +} 1.159 + 1.160 +static JSRuntime * 1.161 +RuntimeForCurrentThread() 1.162 +{ 1.163 + PerThreadData *threadData = TlsPerThreadData.get(); 1.164 + if (!threadData) 1.165 + return nullptr; 1.166 + 1.167 + return threadData->runtimeIfOnOwnerThread(); 1.168 +} 1.169 +#endif // !defined(XP_MACOSX) 1.170 + 1.171 +// Crashing inside the signal handler can cause the handler to be recursively 1.172 +// invoked, eventually blowing the stack without actually showing a crash 1.173 +// report dialog via Breakpad. To guard against this we watch for such 1.174 +// recursion and fall through to the next handler immediately rather than 1.175 +// trying to handle it. 1.176 +class AutoSetHandlingSignal 1.177 +{ 1.178 + JSRuntime *rt; 1.179 + 1.180 + public: 1.181 + AutoSetHandlingSignal(JSRuntime *rt) 1.182 + : rt(rt) 1.183 + { 1.184 + JS_ASSERT(!rt->handlingSignal); 1.185 + rt->handlingSignal = true; 1.186 + } 1.187 + 1.188 + ~AutoSetHandlingSignal() 1.189 + { 1.190 + JS_ASSERT(rt->handlingSignal); 1.191 + rt->handlingSignal = false; 1.192 + } 1.193 +}; 1.194 + 1.195 +#if defined(JS_CODEGEN_X64) 1.196 +template <class T> 1.197 +static void 1.198 +SetXMMRegToNaN(bool isFloat32, T *xmm_reg) 1.199 +{ 1.200 + if (isFloat32) { 1.201 + JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); 1.202 + float *floats = reinterpret_cast<float*>(xmm_reg); 1.203 + floats[0] = GenericNaN(); 1.204 + floats[1] = 0; 1.205 + floats[2] = 0; 1.206 + floats[3] = 0; 1.207 + } else { 1.208 + JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double)); 1.209 + double *dbls = reinterpret_cast<double*>(xmm_reg); 1.210 + dbls[0] = GenericNaN(); 1.211 + dbls[1] = 0; 1.212 + } 1.213 +} 1.214 + 1.215 +struct GetHeapAccessOffset 1.216 +{ 1.217 + const AsmJSModule &module; 1.218 + explicit GetHeapAccessOffset(const AsmJSModule &module) : module(module) {} 1.219 + uintptr_t operator[](size_t index) const { 1.220 + return module.heapAccess(index).offset(); 1.221 + } 1.222 +}; 1.223 + 1.224 +// Perform a binary search on the projected offsets of the known heap accesses 1.225 +// in the module. 1.226 +static const AsmJSHeapAccess * 1.227 +LookupHeapAccess(const AsmJSModule &module, uint8_t *pc) 1.228 +{ 1.229 + JS_ASSERT(module.containsPC(pc)); 1.230 + 1.231 + uintptr_t pcOff = pc - module.codeBase(); 1.232 + 1.233 + size_t match; 1.234 + if (!BinarySearch(GetHeapAccessOffset(module), 0, module.numHeapAccesses(), pcOff, &match)) 1.235 + return nullptr; 1.236 + 1.237 + return &module.heapAccess(match); 1.238 +} 1.239 +#endif 1.240 + 1.241 +#if defined(XP_WIN) 1.242 +# include "jswin.h" 1.243 +#else 1.244 +# include <signal.h> 1.245 +# include <sys/mman.h> 1.246 +#endif 1.247 + 1.248 +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 1.249 +# include <sys/ucontext.h> // for ucontext_t, mcontext_t 1.250 +#endif 1.251 + 1.252 +#if defined(JS_CODEGEN_X64) 1.253 +# if defined(__DragonFly__) 1.254 +# include <machine/npx.h> // for union savefpu 1.255 +# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ 1.256 + defined(__NetBSD__) || defined(__OpenBSD__) 1.257 +# include <machine/fpu.h> // for struct savefpu/fxsave64 1.258 +# endif 1.259 +#endif 1.260 + 1.261 +#if defined(ANDROID) 1.262 +// Not all versions of the Android NDK define ucontext_t or mcontext_t. 1.263 +// Detect this and provide custom but compatible definitions. Note that these 1.264 +// follow the GLibc naming convention to access register values from 1.265 +// mcontext_t. 1.266 +// 1.267 +// See: https://chromiumcodereview.appspot.com/10829122/ 1.268 +// See: http://code.google.com/p/android/issues/detail?id=34784 1.269 +# if !defined(__BIONIC_HAVE_UCONTEXT_T) 1.270 +# if defined(__arm__) 1.271 + 1.272 +// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'. 1.273 +// Old versions of the C library <signal.h> didn't define the type. 1.274 +# if !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) 1.275 +# include <asm/sigcontext.h> 1.276 +# endif 1.277 + 1.278 +typedef struct sigcontext mcontext_t; 1.279 + 1.280 +typedef struct ucontext { 1.281 + uint32_t uc_flags; 1.282 + struct ucontext* uc_link; 1.283 + stack_t uc_stack; 1.284 + mcontext_t uc_mcontext; 1.285 + // Other fields are not used so don't define them here. 1.286 +} ucontext_t; 1.287 + 1.288 +# elif defined(__i386__) 1.289 +// x86 version for Android. 1.290 +typedef struct { 1.291 + uint32_t gregs[19]; 1.292 + void* fpregs; 1.293 + uint32_t oldmask; 1.294 + uint32_t cr2; 1.295 +} mcontext_t; 1.296 + 1.297 +typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks 1.298 +typedef struct ucontext { 1.299 + uint32_t uc_flags; 1.300 + struct ucontext* uc_link; 1.301 + stack_t uc_stack; 1.302 + mcontext_t uc_mcontext; 1.303 + // Other fields are not used by V8, don't define them here. 1.304 +} ucontext_t; 1.305 +enum { REG_EIP = 14 }; 1.306 +# endif // defined(__i386__) 1.307 +# endif // !defined(__BIONIC_HAVE_UCONTEXT_T) 1.308 +#endif // defined(ANDROID) 1.309 + 1.310 +#if defined(ANDROID) && defined(MOZ_LINKER) 1.311 +// Apparently, on some Android systems, the signal handler is always passed 1.312 +// nullptr as the faulting address. This would cause the asm.js signal handler 1.313 +// to think that a safe out-of-bounds access was a nullptr-deref. This 1.314 +// brokenness is already detected by ElfLoader (enabled by MOZ_LINKER), so 1.315 +// reuse that check to disable asm.js compilation on systems where the signal 1.316 +// handler is broken. 1.317 +extern "C" MFBT_API bool IsSignalHandlingBroken(); 1.318 +#else 1.319 +static bool IsSignalHandlingBroken() { return false; } 1.320 +#endif // defined(MOZ_LINKER) 1.321 + 1.322 +#if !defined(XP_WIN) 1.323 +# define CONTEXT ucontext_t 1.324 +#endif 1.325 + 1.326 +#if defined(JS_CPU_X64) 1.327 +# define PC_sig(p) RIP_sig(p) 1.328 +#elif defined(JS_CPU_X86) 1.329 +# define PC_sig(p) EIP_sig(p) 1.330 +#elif defined(JS_CPU_ARM) 1.331 +# define PC_sig(p) R15_sig(p) 1.332 +#endif 1.333 + 1.334 +static bool 1.335 +HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *faultingAddress) 1.336 +{ 1.337 + // If the ARM simulator is enabled, the pc is in the simulator C++ code and 1.338 + // not in the generated code, so we check the simulator's pc manually. Also 1.339 + // note that we can't simply use simulator->set_pc() here because the 1.340 + // simulator could be in the middle of an instruction. On ARM, the signal 1.341 + // handlers are currently only used for Odin code, see bug 964258. 1.342 + 1.343 +#ifdef JS_ARM_SIMULATOR 1.344 + const AsmJSModule &module = activation->module(); 1.345 + if (module.containsPC((void *)rt->mainThread.simulator()->get_pc()) && 1.346 + module.containsPC(faultingAddress)) 1.347 + { 1.348 + activation->setInterrupted(nullptr); 1.349 + int32_t nextpc = int32_t(module.interruptExit()); 1.350 + rt->mainThread.simulator()->set_resume_pc(nextpc); 1.351 + return true; 1.352 + } 1.353 +#endif 1.354 + return false; 1.355 +} 1.356 + 1.357 +#if !defined(XP_MACOSX) 1.358 +static uint8_t ** 1.359 +ContextToPC(CONTEXT *context) 1.360 +{ 1.361 + JS_STATIC_ASSERT(sizeof(PC_sig(context)) == sizeof(void*)); 1.362 + return reinterpret_cast<uint8_t**>(&PC_sig(context)); 1.363 +} 1.364 + 1.365 +# if defined(JS_CODEGEN_X64) 1.366 +static void 1.367 +SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg) 1.368 +{ 1.369 + if (reg.isFloat()) { 1.370 + switch (reg.fpu().code()) { 1.371 + case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break; 1.372 + case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break; 1.373 + case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break; 1.374 + case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 3)); break; 1.375 + case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 4)); break; 1.376 + case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 5)); break; 1.377 + case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 6)); break; 1.378 + case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 7)); break; 1.379 + case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 8)); break; 1.380 + case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 9)); break; 1.381 + case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 10)); break; 1.382 + case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 11)); break; 1.383 + case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 12)); break; 1.384 + case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 13)); break; 1.385 + case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 14)); break; 1.386 + case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 15)); break; 1.387 + default: MOZ_CRASH(); 1.388 + } 1.389 + } else { 1.390 + switch (reg.gpr().code()) { 1.391 + case JSC::X86Registers::eax: RAX_sig(context) = 0; break; 1.392 + case JSC::X86Registers::ecx: RCX_sig(context) = 0; break; 1.393 + case JSC::X86Registers::edx: RDX_sig(context) = 0; break; 1.394 + case JSC::X86Registers::ebx: RBX_sig(context) = 0; break; 1.395 + case JSC::X86Registers::esp: RSP_sig(context) = 0; break; 1.396 + case JSC::X86Registers::ebp: RBP_sig(context) = 0; break; 1.397 + case JSC::X86Registers::esi: RSI_sig(context) = 0; break; 1.398 + case JSC::X86Registers::edi: RDI_sig(context) = 0; break; 1.399 + case JSC::X86Registers::r8: R8_sig(context) = 0; break; 1.400 + case JSC::X86Registers::r9: R9_sig(context) = 0; break; 1.401 + case JSC::X86Registers::r10: R10_sig(context) = 0; break; 1.402 + case JSC::X86Registers::r11: R11_sig(context) = 0; break; 1.403 + case JSC::X86Registers::r12: R12_sig(context) = 0; break; 1.404 + case JSC::X86Registers::r13: R13_sig(context) = 0; break; 1.405 + case JSC::X86Registers::r14: R14_sig(context) = 0; break; 1.406 + case JSC::X86Registers::r15: R15_sig(context) = 0; break; 1.407 + default: MOZ_CRASH(); 1.408 + } 1.409 + } 1.410 +} 1.411 +# endif // JS_CODEGEN_X64 1.412 +#endif // !XP_MACOSX 1.413 + 1.414 +#if defined(XP_WIN) 1.415 + 1.416 +static bool 1.417 +HandleException(PEXCEPTION_POINTERS exception) 1.418 +{ 1.419 + EXCEPTION_RECORD *record = exception->ExceptionRecord; 1.420 + CONTEXT *context = exception->ContextRecord; 1.421 + 1.422 + if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) 1.423 + return false; 1.424 + 1.425 + uint8_t **ppc = ContextToPC(context); 1.426 + uint8_t *pc = *ppc; 1.427 + JS_ASSERT(pc == record->ExceptionAddress); 1.428 + 1.429 + if (record->NumberParameters < 2) 1.430 + return false; 1.431 + 1.432 + void *faultingAddress = (void*)record->ExceptionInformation[1]; 1.433 + 1.434 + JSRuntime *rt = RuntimeForCurrentThread(); 1.435 + 1.436 + // Don't allow recursive handling of signals, see AutoSetHandlingSignal. 1.437 + if (!rt || rt->handlingSignal) 1.438 + return false; 1.439 + AutoSetHandlingSignal handling(rt); 1.440 + 1.441 + if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) 1.442 + return true; 1.443 + 1.444 + AsmJSActivation *activation = InnermostAsmJSActivation(); 1.445 + if (!activation) 1.446 + return false; 1.447 + 1.448 + const AsmJSModule &module = activation->module(); 1.449 + if (!module.containsPC(pc)) 1.450 + return false; 1.451 + 1.452 + // If we faulted trying to execute code in 'module', this must be an 1.453 + // interrupt callback (see RequestInterruptForAsmJSCode). Redirect 1.454 + // execution to a trampoline which will call js::HandleExecutionInterrupt. 1.455 + // The trampoline will jump to activation->resumePC if execution isn't 1.456 + // interrupted. 1.457 + if (module.containsPC(faultingAddress)) { 1.458 + activation->setInterrupted(pc); 1.459 + *ppc = module.interruptExit(); 1.460 + 1.461 + JSRuntime::AutoLockForInterrupt lock(rt); 1.462 + module.unprotectCode(rt); 1.463 + return true; 1.464 + } 1.465 + 1.466 +# if defined(JS_CODEGEN_X64) 1.467 + // These checks aren't necessary, but, since we can, check anyway to make 1.468 + // sure we aren't covering up a real bug. 1.469 + if (!module.maybeHeap() || 1.470 + faultingAddress < module.maybeHeap() || 1.471 + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) 1.472 + { 1.473 + return false; 1.474 + } 1.475 + 1.476 + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); 1.477 + if (!heapAccess) 1.478 + return false; 1.479 + 1.480 + // Also not necessary, but, since we can, do. 1.481 + if (heapAccess->isLoad() != !record->ExceptionInformation[0]) 1.482 + return false; 1.483 + 1.484 + // We now know that this is an out-of-bounds access made by an asm.js 1.485 + // load/store that we should handle. If this is a load, assign the 1.486 + // JS-defined result value to the destination register (ToInt32(undefined) 1.487 + // or ToNumber(undefined), determined by the type of the destination 1.488 + // register) and set the PC to the next op. Upon return from the handler, 1.489 + // execution will resume at this next PC. 1.490 + if (heapAccess->isLoad()) 1.491 + SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg()); 1.492 + *ppc += heapAccess->opLength(); 1.493 + return true; 1.494 +# else 1.495 + return false; 1.496 +# endif 1.497 +} 1.498 + 1.499 +static LONG WINAPI 1.500 +AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception) 1.501 +{ 1.502 + if (HandleException(exception)) 1.503 + return EXCEPTION_CONTINUE_EXECUTION; 1.504 + 1.505 + // No need to worry about calling other handlers, the OS does this for us. 1.506 + return EXCEPTION_CONTINUE_SEARCH; 1.507 +} 1.508 + 1.509 +#elif defined(XP_MACOSX) 1.510 +# include <mach/exc.h> 1.511 + 1.512 +static uint8_t ** 1.513 +ContextToPC(x86_thread_state_t &state) 1.514 +{ 1.515 +# if defined(JS_CODEGEN_X64) 1.516 + JS_STATIC_ASSERT(sizeof(state.uts.ts64.__rip) == sizeof(void*)); 1.517 + return reinterpret_cast<uint8_t**>(&state.uts.ts64.__rip); 1.518 +# else 1.519 + JS_STATIC_ASSERT(sizeof(state.uts.ts32.__eip) == sizeof(void*)); 1.520 + return reinterpret_cast<uint8_t**>(&state.uts.ts32.__eip); 1.521 +# endif 1.522 +} 1.523 + 1.524 +# if defined(JS_CODEGEN_X64) 1.525 +static bool 1.526 +SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state, 1.527 + const AsmJSHeapAccess &heapAccess) 1.528 +{ 1.529 + if (heapAccess.loadedReg().isFloat()) { 1.530 + kern_return_t kret; 1.531 + 1.532 + x86_float_state64_t fstate; 1.533 + unsigned int count = x86_FLOAT_STATE64_COUNT; 1.534 + kret = thread_get_state(rtThread, x86_FLOAT_STATE64, (thread_state_t) &fstate, &count); 1.535 + if (kret != KERN_SUCCESS) 1.536 + return false; 1.537 + 1.538 + bool f32 = heapAccess.isFloat32Load(); 1.539 + switch (heapAccess.loadedReg().fpu().code()) { 1.540 + case JSC::X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break; 1.541 + case JSC::X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break; 1.542 + case JSC::X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break; 1.543 + case JSC::X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break; 1.544 + case JSC::X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break; 1.545 + case JSC::X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break; 1.546 + case JSC::X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break; 1.547 + case JSC::X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break; 1.548 + case JSC::X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break; 1.549 + case JSC::X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break; 1.550 + case JSC::X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break; 1.551 + case JSC::X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break; 1.552 + case JSC::X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break; 1.553 + case JSC::X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break; 1.554 + case JSC::X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break; 1.555 + case JSC::X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break; 1.556 + default: MOZ_CRASH(); 1.557 + } 1.558 + 1.559 + kret = thread_set_state(rtThread, x86_FLOAT_STATE64, (thread_state_t)&fstate, x86_FLOAT_STATE64_COUNT); 1.560 + if (kret != KERN_SUCCESS) 1.561 + return false; 1.562 + } else { 1.563 + switch (heapAccess.loadedReg().gpr().code()) { 1.564 + case JSC::X86Registers::eax: state.__rax = 0; break; 1.565 + case JSC::X86Registers::ecx: state.__rcx = 0; break; 1.566 + case JSC::X86Registers::edx: state.__rdx = 0; break; 1.567 + case JSC::X86Registers::ebx: state.__rbx = 0; break; 1.568 + case JSC::X86Registers::esp: state.__rsp = 0; break; 1.569 + case JSC::X86Registers::ebp: state.__rbp = 0; break; 1.570 + case JSC::X86Registers::esi: state.__rsi = 0; break; 1.571 + case JSC::X86Registers::edi: state.__rdi = 0; break; 1.572 + case JSC::X86Registers::r8: state.__r8 = 0; break; 1.573 + case JSC::X86Registers::r9: state.__r9 = 0; break; 1.574 + case JSC::X86Registers::r10: state.__r10 = 0; break; 1.575 + case JSC::X86Registers::r11: state.__r11 = 0; break; 1.576 + case JSC::X86Registers::r12: state.__r12 = 0; break; 1.577 + case JSC::X86Registers::r13: state.__r13 = 0; break; 1.578 + case JSC::X86Registers::r14: state.__r14 = 0; break; 1.579 + case JSC::X86Registers::r15: state.__r15 = 0; break; 1.580 + default: MOZ_CRASH(); 1.581 + } 1.582 + } 1.583 + return true; 1.584 +} 1.585 +# endif 1.586 + 1.587 +// This definition was generated by mig (the Mach Interface Generator) for the 1.588 +// routine 'exception_raise' (exc.defs). 1.589 +#pragma pack(4) 1.590 +typedef struct { 1.591 + mach_msg_header_t Head; 1.592 + /* start of the kernel processed data */ 1.593 + mach_msg_body_t msgh_body; 1.594 + mach_msg_port_descriptor_t thread; 1.595 + mach_msg_port_descriptor_t task; 1.596 + /* end of the kernel processed data */ 1.597 + NDR_record_t NDR; 1.598 + exception_type_t exception; 1.599 + mach_msg_type_number_t codeCnt; 1.600 + int64_t code[2]; 1.601 +} Request__mach_exception_raise_t; 1.602 +#pragma pack() 1.603 + 1.604 +// The full Mach message also includes a trailer. 1.605 +struct ExceptionRequest 1.606 +{ 1.607 + Request__mach_exception_raise_t body; 1.608 + mach_msg_trailer_t trailer; 1.609 +}; 1.610 + 1.611 +static bool 1.612 +HandleMachException(JSRuntime *rt, const ExceptionRequest &request) 1.613 +{ 1.614 + // Don't allow recursive handling of signals, see AutoSetHandlingSignal. 1.615 + if (rt->handlingSignal) 1.616 + return false; 1.617 + AutoSetHandlingSignal handling(rt); 1.618 + 1.619 + // Get the port of the JSRuntime's thread from the message. 1.620 + mach_port_t rtThread = request.body.thread.name; 1.621 + 1.622 + // Read out the JSRuntime thread's register state. 1.623 + x86_thread_state_t state; 1.624 + unsigned int count = x86_THREAD_STATE_COUNT; 1.625 + kern_return_t kret; 1.626 + kret = thread_get_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, &count); 1.627 + if (kret != KERN_SUCCESS) 1.628 + return false; 1.629 + 1.630 + uint8_t **ppc = ContextToPC(state); 1.631 + uint8_t *pc = *ppc; 1.632 + 1.633 + if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2) 1.634 + return false; 1.635 + 1.636 + void *faultingAddress = (void*)request.body.code[1]; 1.637 + 1.638 + if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) 1.639 + return true; 1.640 + 1.641 + AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread(); 1.642 + if (!activation) 1.643 + return false; 1.644 + 1.645 + const AsmJSModule &module = activation->module(); 1.646 + if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) { 1.647 + JSRuntime::AutoLockForInterrupt lock(rt); 1.648 + module.unprotectCode(rt); 1.649 + return true; 1.650 + } 1.651 + 1.652 + if (!module.containsPC(pc)) 1.653 + return false; 1.654 + 1.655 + // If we faulted trying to execute code in 'module', this must be an 1.656 + // interrupt callback (see RequestInterruptForAsmJSCode). Redirect 1.657 + // execution to a trampoline which will call js::HandleExecutionInterrupt. 1.658 + // The trampoline will jump to activation->resumePC if execution isn't 1.659 + // interrupted. 1.660 + if (module.containsPC(faultingAddress)) { 1.661 + activation->setInterrupted(pc); 1.662 + *ppc = module.interruptExit(); 1.663 + 1.664 + JSRuntime::AutoLockForInterrupt lock(rt); 1.665 + module.unprotectCode(rt); 1.666 + 1.667 + // Update the thread state with the new pc. 1.668 + kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT); 1.669 + return kret == KERN_SUCCESS; 1.670 + } 1.671 + 1.672 +# if defined(JS_CODEGEN_X64) 1.673 + // These checks aren't necessary, but, since we can, check anyway to make 1.674 + // sure we aren't covering up a real bug. 1.675 + if (!module.maybeHeap() || 1.676 + faultingAddress < module.maybeHeap() || 1.677 + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) 1.678 + { 1.679 + return false; 1.680 + } 1.681 + 1.682 + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); 1.683 + if (!heapAccess) 1.684 + return false; 1.685 + 1.686 + // We now know that this is an out-of-bounds access made by an asm.js 1.687 + // load/store that we should handle. If this is a load, assign the 1.688 + // JS-defined result value to the destination register (ToInt32(undefined) 1.689 + // or ToNumber(undefined), determined by the type of the destination 1.690 + // register) and set the PC to the next op. Upon return from the handler, 1.691 + // execution will resume at this next PC. 1.692 + if (heapAccess->isLoad()) { 1.693 + if (!SetRegisterToCoercedUndefined(rtThread, state.uts.ts64, *heapAccess)) 1.694 + return false; 1.695 + } 1.696 + *ppc += heapAccess->opLength(); 1.697 + 1.698 + // Update the thread state with the new pc. 1.699 + kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT); 1.700 + if (kret != KERN_SUCCESS) 1.701 + return false; 1.702 + 1.703 + return true; 1.704 +# else 1.705 + return false; 1.706 +# endif 1.707 +} 1.708 + 1.709 +// Taken from mach_exc in /usr/include/mach/mach_exc.defs. 1.710 +static const mach_msg_id_t sExceptionId = 2405; 1.711 + 1.712 +// The choice of id here is arbitrary, the only constraint is that sQuitId != sExceptionId. 1.713 +static const mach_msg_id_t sQuitId = 42; 1.714 + 1.715 +void 1.716 +AsmJSMachExceptionHandlerThread(void *threadArg) 1.717 +{ 1.718 + JSRuntime *rt = reinterpret_cast<JSRuntime*>(threadArg); 1.719 + mach_port_t port = rt->asmJSMachExceptionHandler.port(); 1.720 + kern_return_t kret; 1.721 + 1.722 + while(true) { 1.723 + ExceptionRequest request; 1.724 + kret = mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request), 1.725 + port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.726 + 1.727 + // If we fail even receiving the message, we can't even send a reply! 1.728 + // Rather than hanging the faulting thread (hanging the browser), crash. 1.729 + if (kret != KERN_SUCCESS) { 1.730 + fprintf(stderr, "AsmJSMachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret); 1.731 + MOZ_CRASH(); 1.732 + } 1.733 + 1.734 + // There are only two messages we should be receiving: an exception 1.735 + // message that occurs when the runtime's thread faults and the quit 1.736 + // message sent when the runtime is shutting down. 1.737 + if (request.body.Head.msgh_id == sQuitId) 1.738 + break; 1.739 + if (request.body.Head.msgh_id != sExceptionId) { 1.740 + fprintf(stderr, "Unexpected msg header id %d\n", (int)request.body.Head.msgh_bits); 1.741 + MOZ_CRASH(); 1.742 + } 1.743 + 1.744 + // Some thread just commited an EXC_BAD_ACCESS and has been suspended by 1.745 + // the kernel. The kernel is waiting for us to reply with instructions. 1.746 + // Our default is the "not handled" reply (by setting the RetCode field 1.747 + // of the reply to KERN_FAILURE) which tells the kernel to continue 1.748 + // searching at the process and system level. If this is an asm.js 1.749 + // expected exception, we handle it and return KERN_SUCCESS. 1.750 + bool handled = HandleMachException(rt, request); 1.751 + kern_return_t replyCode = handled ? KERN_SUCCESS : KERN_FAILURE; 1.752 + 1.753 + // This magic incantation to send a reply back to the kernel was derived 1.754 + // from the exc_server generated by 'mig -v /usr/include/mach/mach_exc.defs'. 1.755 + __Reply__exception_raise_t reply; 1.756 + reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.body.Head.msgh_bits), 0); 1.757 + reply.Head.msgh_size = sizeof(reply); 1.758 + reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port; 1.759 + reply.Head.msgh_local_port = MACH_PORT_NULL; 1.760 + reply.Head.msgh_id = request.body.Head.msgh_id + 100; 1.761 + reply.NDR = NDR_record; 1.762 + reply.RetCode = replyCode; 1.763 + mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL, 1.764 + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.765 + } 1.766 +} 1.767 + 1.768 +AsmJSMachExceptionHandler::AsmJSMachExceptionHandler() 1.769 + : installed_(false), 1.770 + thread_(nullptr), 1.771 + port_(MACH_PORT_NULL) 1.772 +{} 1.773 + 1.774 +void 1.775 +AsmJSMachExceptionHandler::uninstall() 1.776 +{ 1.777 +#ifdef JS_THREADSAFE 1.778 + if (installed_) { 1.779 + thread_port_t thread = mach_thread_self(); 1.780 + kern_return_t kret = thread_set_exception_ports(thread, 1.781 + EXC_MASK_BAD_ACCESS, 1.782 + MACH_PORT_NULL, 1.783 + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 1.784 + THREAD_STATE_NONE); 1.785 + mach_port_deallocate(mach_task_self(), thread); 1.786 + if (kret != KERN_SUCCESS) 1.787 + MOZ_CRASH(); 1.788 + installed_ = false; 1.789 + } 1.790 + if (thread_ != nullptr) { 1.791 + // Break the handler thread out of the mach_msg loop. 1.792 + mach_msg_header_t msg; 1.793 + msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 1.794 + msg.msgh_size = sizeof(msg); 1.795 + msg.msgh_remote_port = port_; 1.796 + msg.msgh_local_port = MACH_PORT_NULL; 1.797 + msg.msgh_reserved = 0; 1.798 + msg.msgh_id = sQuitId; 1.799 + kern_return_t kret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, 1.800 + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.801 + if (kret != KERN_SUCCESS) { 1.802 + fprintf(stderr, "AsmJSMachExceptionHandler: failed to send quit message: %d\n", (int)kret); 1.803 + MOZ_CRASH(); 1.804 + } 1.805 + 1.806 + // Wait for the handler thread to complete before deallocating the port. 1.807 + PR_JoinThread(thread_); 1.808 + thread_ = nullptr; 1.809 + } 1.810 + if (port_ != MACH_PORT_NULL) { 1.811 + DebugOnly<kern_return_t> kret = mach_port_destroy(mach_task_self(), port_); 1.812 + JS_ASSERT(kret == KERN_SUCCESS); 1.813 + port_ = MACH_PORT_NULL; 1.814 + } 1.815 +#else 1.816 + JS_ASSERT(!installed_); 1.817 +#endif 1.818 +} 1.819 + 1.820 +bool 1.821 +AsmJSMachExceptionHandler::install(JSRuntime *rt) 1.822 +{ 1.823 +#ifdef JS_THREADSAFE 1.824 + JS_ASSERT(!installed()); 1.825 + kern_return_t kret; 1.826 + mach_port_t thread; 1.827 + 1.828 + // Get a port which can send and receive data. 1.829 + kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port_); 1.830 + if (kret != KERN_SUCCESS) 1.831 + goto error; 1.832 + kret = mach_port_insert_right(mach_task_self(), port_, port_, MACH_MSG_TYPE_MAKE_SEND); 1.833 + if (kret != KERN_SUCCESS) 1.834 + goto error; 1.835 + 1.836 + // Create a thread to block on reading port_. 1.837 + thread_ = PR_CreateThread(PR_USER_THREAD, AsmJSMachExceptionHandlerThread, rt, 1.838 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 1.839 + if (!thread_) 1.840 + goto error; 1.841 + 1.842 + // Direct exceptions on this thread to port_ (and thus our handler thread). 1.843 + // Note: we are totally clobbering any existing *thread* exception ports and 1.844 + // not even attempting to forward. Breakpad and gdb both use the *process* 1.845 + // exception ports which are only called if the thread doesn't handle the 1.846 + // exception, so we should be fine. 1.847 + thread = mach_thread_self(); 1.848 + kret = thread_set_exception_ports(thread, 1.849 + EXC_MASK_BAD_ACCESS, 1.850 + port_, 1.851 + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 1.852 + THREAD_STATE_NONE); 1.853 + mach_port_deallocate(mach_task_self(), thread); 1.854 + if (kret != KERN_SUCCESS) 1.855 + goto error; 1.856 + 1.857 + installed_ = true; 1.858 + return true; 1.859 + 1.860 + error: 1.861 + uninstall(); 1.862 + return false; 1.863 +#else 1.864 + return false; 1.865 +#endif 1.866 +} 1.867 + 1.868 +#else // If not Windows or Mac, assume Unix 1.869 + 1.870 +// Be very cautious and default to not handling; we don't want to accidentally 1.871 +// silence real crashes from real bugs. 1.872 +static bool 1.873 +HandleSignal(int signum, siginfo_t *info, void *ctx) 1.874 +{ 1.875 + CONTEXT *context = (CONTEXT *)ctx; 1.876 + uint8_t **ppc = ContextToPC(context); 1.877 + uint8_t *pc = *ppc; 1.878 + 1.879 + void *faultingAddress = info->si_addr; 1.880 + 1.881 + JSRuntime *rt = RuntimeForCurrentThread(); 1.882 + 1.883 + // Don't allow recursive handling of signals, see AutoSetHandlingSignal. 1.884 + if (!rt || rt->handlingSignal) 1.885 + return false; 1.886 + AutoSetHandlingSignal handling(rt); 1.887 + 1.888 + if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) 1.889 + return true; 1.890 + 1.891 + AsmJSActivation *activation = InnermostAsmJSActivation(); 1.892 + if (!activation) 1.893 + return false; 1.894 + 1.895 + const AsmJSModule &module = activation->module(); 1.896 + if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) { 1.897 + JSRuntime::AutoLockForInterrupt lock(rt); 1.898 + module.unprotectCode(rt); 1.899 + return true; 1.900 + } 1.901 + 1.902 + if (!module.containsPC(pc)) 1.903 + return false; 1.904 + 1.905 + // If we faulted trying to execute code in 'module', this must be an 1.906 + // interrupt callback (see RequestInterruptForAsmJSCode). Redirect 1.907 + // execution to a trampoline which will call js::HandleExecutionInterrupt. 1.908 + // The trampoline will jump to activation->resumePC if execution isn't 1.909 + // interrupted. 1.910 + if (module.containsPC(faultingAddress)) { 1.911 + activation->setInterrupted(pc); 1.912 + *ppc = module.interruptExit(); 1.913 + 1.914 + JSRuntime::AutoLockForInterrupt lock(rt); 1.915 + module.unprotectCode(rt); 1.916 + return true; 1.917 + } 1.918 + 1.919 +# if defined(JS_CODEGEN_X64) 1.920 + // These checks aren't necessary, but, since we can, check anyway to make 1.921 + // sure we aren't covering up a real bug. 1.922 + if (!module.maybeHeap() || 1.923 + faultingAddress < module.maybeHeap() || 1.924 + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) 1.925 + { 1.926 + return false; 1.927 + } 1.928 + 1.929 + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); 1.930 + if (!heapAccess) 1.931 + return false; 1.932 + 1.933 + // We now know that this is an out-of-bounds access made by an asm.js 1.934 + // load/store that we should handle. If this is a load, assign the 1.935 + // JS-defined result value to the destination register (ToInt32(undefined) 1.936 + // or ToNumber(undefined), determined by the type of the destination 1.937 + // register) and set the PC to the next op. Upon return from the handler, 1.938 + // execution will resume at this next PC. 1.939 + if (heapAccess->isLoad()) 1.940 + SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg()); 1.941 + *ppc += heapAccess->opLength(); 1.942 + return true; 1.943 +# else 1.944 + return false; 1.945 +# endif 1.946 +} 1.947 + 1.948 +static struct sigaction sPrevHandler; 1.949 + 1.950 +static void 1.951 +AsmJSFaultHandler(int signum, siginfo_t *info, void *context) 1.952 +{ 1.953 + if (HandleSignal(signum, info, context)) 1.954 + return; 1.955 + 1.956 + // This signal is not for any asm.js code we expect, so we need to forward 1.957 + // the signal to the next handler. If there is no next handler (SIG_IGN or 1.958 + // SIG_DFL), then it's time to crash. To do this, we set the signal back to 1.959 + // its original disposition and return. This will cause the faulting op to 1.960 + // be re-executed which will crash in the normal way. The advantage of 1.961 + // doing this to calling _exit() is that we remove ourselves from the crash 1.962 + // stack which improves crash reports. If there is a next handler, call it. 1.963 + // It will either crash synchronously, fix up the instruction so that 1.964 + // execution can continue and return, or trigger a crash by returning the 1.965 + // signal to it's original disposition and returning. 1.966 + // 1.967 + // Note: the order of these tests matter. 1.968 + if (sPrevHandler.sa_flags & SA_SIGINFO) 1.969 + sPrevHandler.sa_sigaction(signum, info, context); 1.970 + else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) 1.971 + sigaction(signum, &sPrevHandler, nullptr); 1.972 + else 1.973 + sPrevHandler.sa_handler(signum); 1.974 +} 1.975 +#endif 1.976 + 1.977 +#if !defined(XP_MACOSX) 1.978 +static bool sHandlersInstalled = false; 1.979 +#endif 1.980 + 1.981 +bool 1.982 +js::EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt) 1.983 +{ 1.984 + if (IsSignalHandlingBroken()) 1.985 + return false; 1.986 + 1.987 +#if defined(XP_MACOSX) 1.988 + // On OSX, each JSRuntime gets its own handler. 1.989 + return rt->asmJSMachExceptionHandler.installed() || rt->asmJSMachExceptionHandler.install(rt); 1.990 +#else 1.991 + // Assume Windows or Unix. For these platforms, there is a single, 1.992 + // process-wide signal handler installed. Take care to only install it once. 1.993 + if (sHandlersInstalled) 1.994 + return true; 1.995 + 1.996 +# if defined(XP_WIN) 1.997 + if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler)) 1.998 + return false; 1.999 +# else 1.1000 + // Assume Unix. SA_NODEFER allows us to reenter the signal handler if we 1.1001 + // crash while handling the signal, and fall through to the Breakpad 1.1002 + // handler by testing handlingSignal. 1.1003 + struct sigaction sigAction; 1.1004 + sigAction.sa_flags = SA_SIGINFO | SA_NODEFER; 1.1005 + sigAction.sa_sigaction = &AsmJSFaultHandler; 1.1006 + sigemptyset(&sigAction.sa_mask); 1.1007 + if (sigaction(SIGSEGV, &sigAction, &sPrevHandler)) 1.1008 + return false; 1.1009 +# endif 1.1010 + 1.1011 + sHandlersInstalled = true; 1.1012 +#endif 1.1013 + return true; 1.1014 +} 1.1015 + 1.1016 +// To interrupt execution of a JSRuntime, any thread may call 1.1017 +// JS_RequestInterruptCallback (JSRuntime::requestInterruptCallback from inside 1.1018 +// the engine). In the simplest case, this sets some state that is polled at 1.1019 +// regular intervals (function prologues, loop headers). For tight loops, this 1.1020 +// poses non-trivial overhead. For asm.js, we can do better: when another 1.1021 +// thread requests an interrupt, we simply mprotect all of the innermost asm.js 1.1022 +// module activation's code. This will trigger a SIGSEGV, taking us into 1.1023 +// AsmJSFaultHandler. From there, we can manually redirect execution to call 1.1024 +// js::HandleExecutionInterrupt. The memory is un-protected from the signal 1.1025 +// handler after control flow is redirected. 1.1026 +void 1.1027 +js::RequestInterruptForAsmJSCode(JSRuntime *rt) 1.1028 +{ 1.1029 + JS_ASSERT(rt->currentThreadOwnsInterruptLock()); 1.1030 + 1.1031 + AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread(); 1.1032 + if (!activation) 1.1033 + return; 1.1034 + 1.1035 + activation->module().protectCode(rt); 1.1036 +} 1.1037 + 1.1038 +#if defined(MOZ_ASAN) && defined(JS_STANDALONE) 1.1039 +// Usually, this definition is found in mozglue (see mozglue/build/AsanOptions.cpp). 1.1040 +// However, when doing standalone JS builds, mozglue is not used and we must ensure 1.1041 +// that we still allow custom SIGSEGV handlers for asm.js and ion to work correctly. 1.1042 +extern "C" MOZ_ASAN_BLACKLIST 1.1043 +const char* __asan_default_options() { 1.1044 + return "allow_user_segv_handler=1"; 1.1045 +} 1.1046 +#endif