From ea143386f2acacda83f5a9b6abcc27db24dc0029 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 11 Nov 2014 08:36:52 -0600 Subject: [PATCH] Bug 1091912 - stop using mprotect to halt Ion/asm.js execution (r=bhackett) --HG-- extra : rebase_source : 3642ce948e577bca54008d3c55206184b107d023 --- js/src/asmjs/AsmJSModule.cpp | 95 +---- js/src/asmjs/AsmJSModule.h | 10 - js/src/asmjs/AsmJSSignalHandlers.cpp | 519 ++++++++++++------------ js/src/asmjs/AsmJSSignalHandlers.h | 13 +- js/src/jit/CodeGenerator.cpp | 14 - js/src/jit/ExecutableAllocator.cpp | 26 -- js/src/jit/ExecutableAllocator.h | 8 - js/src/jit/ExecutableAllocatorPosix.cpp | 15 - js/src/jit/ExecutableAllocatorWin.cpp | 16 - js/src/jit/Ion.cpp | 136 +------ js/src/jit/Ion.h | 2 - js/src/jit/IonLinker.h | 4 - js/src/jit/JitCompartment.h | 41 +- js/src/jit/VMFunctions.cpp | 14 +- js/src/jsapi.cpp | 2 +- js/src/jscntxt.h | 1 - js/src/jscompartment.cpp | 17 +- js/src/jsgc.cpp | 23 +- js/src/vm/ForkJoin.cpp | 4 +- js/src/vm/HelperThreads.cpp | 2 +- js/src/vm/Runtime.cpp | 50 +-- js/src/vm/Runtime.h | 64 +-- js/src/vm/Stack.cpp | 9 +- 23 files changed, 366 insertions(+), 719 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index bae68a13230f..732657548873 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -54,36 +54,6 @@ using mozilla::PodEqual; using mozilla::Compression::LZ4; using mozilla::Swap; -// At any time, the executable code of an asm.js module can be protected (as -// part of RequestInterruptForAsmJSCode). When we touch the executable outside -// of executing it (which the AsmJSFaultHandler will correctly handle), we need -// to guard against this by unprotecting the code (if it has been protected) and -// preventing it from being protected while we are touching it. -class AutoUnprotectCode -{ - JSRuntime *rt_; - JSRuntime::AutoLockForInterrupt lock_; - const AsmJSModule &module_; - const bool protectedBefore_; - - public: - AutoUnprotectCode(JSContext *cx, const AsmJSModule &module) - : rt_(cx->runtime()), - lock_(rt_), - module_(module), - protectedBefore_(module_.codeIsProtected(rt_)) - { - if (protectedBefore_) - module_.unprotectCode(rt_); - } - - ~AutoUnprotectCode() - { - if (protectedBefore_) - module_.protectCode(rt_); - } -}; - static uint8_t * AllocateExecutableMemory(ExclusiveContext *cx, size_t bytes) { @@ -113,8 +83,7 @@ AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t dynamicallyLinked_(false), loadedFromCache_(false), profilingEnabled_(false), - interrupted_(false), - codeIsProtected_(false) + interrupted_(false) { mozilla::PodZero(&pod); pod.funcPtrTableAndExitBytes_ = SIZE_MAX; @@ -830,7 +799,6 @@ AsmJSModule::initHeap(Handle heap, JSContext *cx #endif } -// This method assumes the caller has a live AutoUnprotectCode. void AsmJSModule::restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer) { @@ -852,7 +820,6 @@ AsmJSModule::restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBu heapDatum() = nullptr; } -// This method assumes the caller has a live AutoUnprotectCode. void AsmJSModule::restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer, uint8_t *prevCode, @@ -907,7 +874,6 @@ AsmJSModule::detachHeap(JSContext *cx) MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI || activation()->exitReason() == AsmJSExit::Reason_SlowFFI); - AutoUnprotectCode auc(cx, *this); restoreHeapToInitialState(maybeHeap_); MOZ_ASSERT(hasDetachedHeap()); @@ -1568,8 +1534,6 @@ AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor) bool AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) const { - AutoUnprotectCode auc(cx, *this); - *moduleOut = cx->new_(scriptSource_, srcStart_, srcBodyStart_, pod.strict_, pod.usesSignalHandlers_); if (!*moduleOut) @@ -1628,7 +1592,6 @@ AsmJSModule::changeHeap(Handle newHeap, JSContext *cx) if (interrupted_) return false; - AutoUnprotectCode auc(cx, *this); restoreHeapToInitialState(maybeHeap_); initHeap(newHeap, cx); return true; @@ -1669,9 +1632,6 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx) AutoFlushICache afc("AsmJSModule::setProfilingEnabled"); setAutoFlushICacheRange(); - // To enable profiling, we need to patch 3 kinds of things: - AutoUnprotectCode auc(cx, *this); - // Patch all internal (asm.js->asm.js) callsites to call the profiling // prologues: for (size_t i = 0; i < callSites_.length(); i++) { @@ -1818,59 +1778,6 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx) profilingEnabled_ = enabled; } -void -AsmJSModule::protectCode(JSRuntime *rt) const -{ - MOZ_ASSERT(isDynamicallyLinked()); - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - - codeIsProtected_ = true; - - if (!pod.functionBytes_) - return; - - // Technically, we should be able to only take away the execute permissions, - // however this seems to break our emulators which don't always check - // execute permissions while executing code. -#if defined(XP_WIN) - DWORD oldProtect; - if (!VirtualProtect(codeBase(), functionBytes(), PAGE_NOACCESS, &oldProtect)) - MOZ_CRASH(); -#else // assume Unix - if (mprotect(codeBase(), functionBytes(), PROT_NONE)) - MOZ_CRASH(); -#endif -} - -void -AsmJSModule::unprotectCode(JSRuntime *rt) const -{ - MOZ_ASSERT(isDynamicallyLinked()); - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - - codeIsProtected_ = false; - - if (!pod.functionBytes_) - return; - -#if defined(XP_WIN) - DWORD oldProtect; - if (!VirtualProtect(codeBase(), functionBytes(), PAGE_EXECUTE_READWRITE, &oldProtect)) - MOZ_CRASH(); -#else // assume Unix - if (mprotect(codeBase(), functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC)) - MOZ_CRASH(); -#endif -} - -bool -AsmJSModule::codeIsProtected(JSRuntime *rt) const -{ - MOZ_ASSERT(isDynamicallyLinked()); - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - return codeIsProtected_; -} - static bool GetCPUID(uint32_t *cpuId) { diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index 3443cd8da932..49861872da97 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -826,10 +826,6 @@ class AsmJSModule bool profilingEnabled_; bool interrupted_; - // This field is accessed concurrently when requesting an interrupt. - // Access must be synchronized via the runtime's interrupt lock. - mutable bool codeIsProtected_; - void restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer); void restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer, uint8_t *prevCode, ExclusiveContext *cx); @@ -1529,12 +1525,6 @@ class AsmJSModule MOZ_ASSERT(isDynamicallyLinked()); interrupted_ = interrupted; } - - // Additionally, these functions may only be called while holding the - // runtime's interrupt lock. - void protectCode(JSRuntime *rt) const; - void unprotectCode(JSRuntime *rt) const; - bool codeIsProtected(JSRuntime *rt) const; }; // Store the just-parsed module in the cache using AsmJSCacheOps. diff --git a/js/src/asmjs/AsmJSSignalHandlers.cpp b/js/src/asmjs/AsmJSSignalHandlers.cpp index 2fae613a9abd..dba220a7a4b5 100644 --- a/js/src/asmjs/AsmJSSignalHandlers.cpp +++ b/js/src/asmjs/AsmJSSignalHandlers.cpp @@ -19,6 +19,7 @@ #include "asmjs/AsmJSSignalHandlers.h" #include "mozilla/DebugOnly.h" +#include "mozilla/PodOperations.h" #include "asmjs/AsmJSModule.h" #include "vm/Runtime.h" @@ -28,6 +29,51 @@ using namespace js::jit; using JS::GenericNaN; using mozilla::DebugOnly; +using mozilla::PodArrayZero; + +#if defined(ANDROID) +# include +# if defined(MOZ_LINKER) +extern "C" MFBT_API bool IsSignalHandlingBroken(); +# endif +#endif + +// For platforms where the signal/exception handler runs on the same +// thread/stack as the victim (Unix and Windows), we can use TLS to find any +// currently executing asm.js code. +static JSRuntime * +RuntimeForCurrentThread() +{ + PerThreadData *threadData = TlsPerThreadData.get(); + if (!threadData) + return nullptr; + + return threadData->runtimeIfOnOwnerThread(); +} + +// Crashing inside the signal handler can cause the handler to be recursively +// invoked, eventually blowing the stack without actually showing a crash +// report dialog via Breakpad. To guard against this we watch for such +// recursion and fall through to the next handler immediately rather than +// trying to handle it. +class AutoSetHandlingSignal +{ + JSRuntime *rt; + + public: + explicit AutoSetHandlingSignal(JSRuntime *rt) + : rt(rt) + { + MOZ_ASSERT(!rt->handlingSignal); + rt->handlingSignal = true; + } + + ~AutoSetHandlingSignal() + { + MOZ_ASSERT(rt->handlingSignal); + rt->handlingSignal = false; + } +}; #if defined(XP_WIN) # define XMM_sig(p,i) ((p)->Xmm##i) @@ -152,71 +198,12 @@ using mozilla::DebugOnly; # define R15_sig(p) ((p)->uc_mcontext.mc_r15) # endif #elif defined(XP_MACOSX) -// Mach requires special treatment. +# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip) +# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip) #else # error "Don't know how to read/write to the thread state via the mcontext_t." #endif -// For platforms where the signal/exception handler runs on the same -// thread/stack as the victim (Unix and Windows), we can use TLS to find any -// currently executing asm.js code. -#if !defined(XP_MACOSX) -static JSRuntime * -RuntimeForCurrentThread() -{ - PerThreadData *threadData = TlsPerThreadData.get(); - if (!threadData) - return nullptr; - - return threadData->runtimeIfOnOwnerThread(); -} -#endif // !defined(XP_MACOSX) - -// Crashing inside the signal handler can cause the handler to be recursively -// invoked, eventually blowing the stack without actually showing a crash -// report dialog via Breakpad. To guard against this we watch for such -// recursion and fall through to the next handler immediately rather than -// trying to handle it. -class AutoSetHandlingSignal -{ - JSRuntime *rt; - - public: - explicit AutoSetHandlingSignal(JSRuntime *rt) - : rt(rt) - { - MOZ_ASSERT(!rt->handlingSignal); - rt->handlingSignal = true; - } - - ~AutoSetHandlingSignal() - { - MOZ_ASSERT(rt->handlingSignal); - rt->handlingSignal = false; - } -}; - -#if defined(JS_CODEGEN_X64) -template -static void -SetXMMRegToNaN(bool isFloat32, T *xmm_reg) -{ - if (isFloat32) { - JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); - float *floats = reinterpret_cast(xmm_reg); - floats[0] = GenericNaN(); - floats[1] = 0; - floats[2] = 0; - floats[3] = 0; - } else { - JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double)); - double *dbls = reinterpret_cast(xmm_reg); - dbls[0] = GenericNaN(); - dbls[1] = 0; - } -} -#endif - #if defined(XP_WIN) # include "jswin.h" #else @@ -228,7 +215,7 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_reg) # include // for ucontext_t, mcontext_t #endif -#if defined(JS_CODEGEN_X64) +#if defined(JS_CPU_X64) # if defined(__DragonFly__) # include // for union savefpu # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ @@ -317,18 +304,6 @@ enum { REG_EIP = 14 }; # endif // !defined(__BIONIC_HAVE_UCONTEXT_T) #endif // defined(ANDROID) -#if defined(ANDROID) && defined(MOZ_LINKER) -// Apparently, on some Android systems, the signal handler is always passed -// nullptr as the faulting address. This would cause the asm.js signal handler -// to think that a safe out-of-bounds access was a nullptr-deref. This -// brokenness is already detected by ElfLoader (enabled by MOZ_LINKER), so -// reuse that check to disable asm.js compilation on systems where the signal -// handler is broken. -extern "C" MFBT_API bool IsSignalHandlingBroken(); -#else -static bool IsSignalHandlingBroken() { return false; } -#endif // defined(MOZ_LINKER) - #if !defined(XP_WIN) # define CONTEXT ucontext_t #endif @@ -343,41 +318,33 @@ static bool IsSignalHandlingBroken() { return false; } # define PC_sig(p) EPC_sig(p) #endif -static bool -HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *faultingAddress) -{ - // If the ARM simulator is enabled, the pc is in the simulator C++ code and - // not in the generated code, so we check the simulator's pc manually. Also - // note that we can't simply use simulator->set_pc() here because the - // simulator could be in the middle of an instruction. On ARM, the signal - // handlers are currently only used for Odin code, see bug 964258. - -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - const AsmJSModule &module = activation->module(); - if (module.containsFunctionPC((void *)rt->mainThread.simulator()->get_pc()) && - module.containsFunctionPC(faultingAddress)) - { - activation->setResumePC(nullptr); - int32_t nextpc = int32_t(module.interruptExit()); - rt->mainThread.simulator()->set_resume_pc(nextpc); - return true; - } -#endif - return false; -} - -#if !defined(XP_MACOSX) static uint8_t ** ContextToPC(CONTEXT *context) { -#ifdef JS_CODEGEN_NONE - MOZ_CRASH(); -#else return reinterpret_cast(&PC_sig(context)); -#endif } -# if defined(JS_CODEGEN_X64) +#if defined(JS_CPU_X64) +template +static void +SetXMMRegToNaN(bool isFloat32, T *xmm_reg) +{ + if (isFloat32) { + JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); + float *floats = reinterpret_cast(xmm_reg); + floats[0] = GenericNaN(); + floats[1] = 0; + floats[2] = 0; + floats[3] = 0; + } else { + JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double)); + double *dbls = reinterpret_cast(xmm_reg); + dbls[0] = GenericNaN(); + dbls[1] = 0; + } +} + +# if !defined(XP_MACOSX) static void SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg) { @@ -423,13 +390,13 @@ SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg) } } } -# endif // JS_CODEGEN_X64 -#endif // !XP_MACOSX +# endif // !XP_MACOSX +#endif // JS_CPU_X64 #if defined(XP_WIN) static bool -HandleException(PEXCEPTION_POINTERS exception) +HandleFault(PEXCEPTION_POINTERS exception) { EXCEPTION_RECORD *record = exception->ExceptionRecord; CONTEXT *context = exception->ContextRecord; @@ -444,19 +411,13 @@ HandleException(PEXCEPTION_POINTERS exception) if (record->NumberParameters < 2) return false; - void *faultingAddress = (void*)record->ExceptionInformation[1]; - - JSRuntime *rt = RuntimeForCurrentThread(); - // Don't allow recursive handling of signals, see AutoSetHandlingSignal. + JSRuntime *rt = RuntimeForCurrentThread(); if (!rt || rt->handlingSignal) return false; AutoSetHandlingSignal handling(rt); - if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) - return true; - - AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation(); + AsmJSActivation *activation = rt->mainThread.asmJSActivationStack(); if (!activation) return false; @@ -464,23 +425,10 @@ HandleException(PEXCEPTION_POINTERS exception) if (!module.containsFunctionPC(pc)) return false; - // If we faulted trying to execute code in 'module', this must be an - // interrupt callback (see RequestInterruptForAsmJSCode). Redirect - // execution to a trampoline which will call js::HandleExecutionInterrupt. - // The trampoline will jump to activation->resumePC if execution isn't - // interrupted. - if (module.containsFunctionPC(faultingAddress)) { - activation->setResumePC(pc); - *ppc = module.interruptExit(); - - JSRuntime::AutoLockForInterrupt lock(rt); - module.unprotectCode(rt); - return true; - } - -# if defined(JS_CODEGEN_X64) +# if defined(JS_CPU_X64) // These checks aren't necessary, but, since we can, check anyway to make // sure we aren't covering up a real bug. + void *faultingAddress = (void*)record->ExceptionInformation[1]; if (!module.maybeHeap() || faultingAddress < module.maybeHeap() || faultingAddress >= module.maybeHeap() + AsmJSMappedSize) @@ -512,9 +460,9 @@ HandleException(PEXCEPTION_POINTERS exception) } static LONG WINAPI -AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception) +AsmJSFaultHandler(LPEXCEPTION_POINTERS exception) { - if (HandleException(exception)) + if (HandleFault(exception)) return EXCEPTION_CONTINUE_EXECUTION; // No need to worry about calling other handlers, the OS does this for us. @@ -527,18 +475,16 @@ AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception) static uint8_t ** ContextToPC(x86_thread_state_t &state) { -# if defined(JS_CODEGEN_X64) +# if defined(JS_CPU_X64) JS_STATIC_ASSERT(sizeof(state.uts.ts64.__rip) == sizeof(void*)); return reinterpret_cast(&state.uts.ts64.__rip); -# elif defined(JS_CODEGEN_NONE) - MOZ_CRASH(); # else JS_STATIC_ASSERT(sizeof(state.uts.ts32.__eip) == sizeof(void*)); return reinterpret_cast(&state.uts.ts32.__eip); # endif } -# if defined(JS_CODEGEN_X64) +# if defined(JS_CPU_X64) static bool SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state, const AsmJSHeapAccess &heapAccess) @@ -650,45 +596,18 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request) if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2) return false; - void *faultingAddress = (void*)request.body.code[1]; - - if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) - return true; - AsmJSActivation *activation = rt->mainThread.asmJSActivationStack(); if (!activation) return false; const AsmJSModule &module = activation->module(); - if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) { - JSRuntime::AutoLockForInterrupt lock(rt); - module.unprotectCode(rt); - return true; - } - if (!module.containsFunctionPC(pc)) return false; - // If we faulted trying to execute code in 'module', this must be an - // interrupt callback (see RequestInterruptForAsmJSCode). Redirect - // execution to a trampoline which will call js::HandleExecutionInterrupt. - // The trampoline will jump to activation->resumePC if execution isn't - // interrupted. - if (module.containsFunctionPC(faultingAddress)) { - activation->setResumePC(pc); - *ppc = module.interruptExit(); - - JSRuntime::AutoLockForInterrupt lock(rt); - module.unprotectCode(rt); - - // Update the thread state with the new pc. - kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT); - return kret == KERN_SUCCESS; - } - -# if defined(JS_CODEGEN_X64) +# if defined(JS_CPU_X64) // These checks aren't necessary, but, since we can, check anyway to make // sure we aren't covering up a real bug. + void *faultingAddress = (void*)request.body.code[1]; if (!module.maybeHeap() || faultingAddress < module.maybeHeap() || faultingAddress >= module.maybeHeap() + AsmJSMappedSize) @@ -879,55 +798,30 @@ AsmJSMachExceptionHandler::install(JSRuntime *rt) // Be very cautious and default to not handling; we don't want to accidentally // silence real crashes from real bugs. static bool -HandleSignal(int signum, siginfo_t *info, void *ctx) +HandleFault(int signum, siginfo_t *info, void *ctx) { CONTEXT *context = (CONTEXT *)ctx; uint8_t **ppc = ContextToPC(context); uint8_t *pc = *ppc; - void *faultingAddress = info->si_addr; - - JSRuntime *rt = RuntimeForCurrentThread(); - // Don't allow recursive handling of signals, see AutoSetHandlingSignal. + JSRuntime *rt = RuntimeForCurrentThread(); if (!rt || rt->handlingSignal) return false; AutoSetHandlingSignal handling(rt); - if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress)) - return true; - - AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation(); + AsmJSActivation *activation = rt->mainThread.asmJSActivationStack(); if (!activation) return false; const AsmJSModule &module = activation->module(); - if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) { - JSRuntime::AutoLockForInterrupt lock(rt); - module.unprotectCode(rt); - return true; - } - if (!module.containsFunctionPC(pc)) return false; - // If we faulted trying to execute code in 'module', this must be an - // interrupt callback (see RequestInterruptForAsmJSCode). Redirect - // execution to a trampoline which will call js::HandleExecutionInterrupt. - // The trampoline will jump to activation->resumePC if execution isn't - // interrupted. - if (module.containsFunctionPC(faultingAddress)) { - activation->setResumePC(pc); - *ppc = module.interruptExit(); - - JSRuntime::AutoLockForInterrupt lock(rt); - module.unprotectCode(rt); - return true; - } - -# if defined(JS_CODEGEN_X64) +# if defined(JS_CPU_X64) // These checks aren't necessary, but, since we can, check anyway to make // sure we aren't covering up a real bug. + void *faultingAddress = info->si_addr; if (!module.maybeHeap() || faultingAddress < module.maybeHeap() || faultingAddress >= module.maybeHeap() + AsmJSMappedSize) @@ -954,12 +848,12 @@ HandleSignal(int signum, siginfo_t *info, void *ctx) # endif } -static struct sigaction sPrevHandler; +static struct sigaction sPrevSEGVHandler; static void AsmJSFaultHandler(int signum, siginfo_t *info, void *context) { - if (HandleSignal(signum, info, context)) + if (HandleFault(signum, info, context)) return; // This signal is not for any asm.js code we expect, so we need to forward @@ -974,90 +868,201 @@ AsmJSFaultHandler(int signum, siginfo_t *info, void *context) // signal to it's original disposition and returning. // // Note: the order of these tests matter. - if (sPrevHandler.sa_flags & SA_SIGINFO) - sPrevHandler.sa_sigaction(signum, info, context); - else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) - sigaction(signum, &sPrevHandler, nullptr); + if (sPrevSEGVHandler.sa_flags & SA_SIGINFO) + sPrevSEGVHandler.sa_sigaction(signum, info, context); + else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN) + sigaction(signum, &sPrevSEGVHandler, nullptr); else - sPrevHandler.sa_handler(signum); + sPrevSEGVHandler.sa_handler(signum); } #endif -#if !defined(XP_MACOSX) -static bool sInstalledHandlers = false; +static void +RedirectIonBackedgesToInterruptCheck(JSRuntime *rt) +{ + if (jit::JitRuntime *jitRuntime = rt->jitRuntime()) { + // If the backedge list is being mutated, the pc must be in C++ code and + // thus not in a JIT iloop. We assume that the interrupt flag will be + // checked at least once before entering JIT code (if not, no big deal; + // the browser will just request another interrupt in a second). + if (!jitRuntime->mutatingBackedgeList()) + jitRuntime->patchIonBackedges(rt, jit::JitRuntime::BackedgeInterruptCheck); + } +} + +static void +RedirectJitCodeToInterruptCheck(JSRuntime *rt, CONTEXT *context) +{ + RedirectIonBackedgesToInterruptCheck(rt); + + if (AsmJSActivation *activation = rt->mainThread.asmJSActivationStack()) { + const AsmJSModule &module = activation->module(); + +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + if (module.containsFunctionPC((void*)rt->mainThread.simulator()->get_pc())) + rt->mainThread.simulator()->set_resume_pc(int32_t(module.interruptExit())); +#endif + + uint8_t **ppc = ContextToPC(context); + uint8_t *pc = *ppc; + if (module.containsFunctionPC(pc)) { + activation->setResumePC(pc); + *ppc = module.interruptExit(); + } + } +} + +#if !defined(XP_WIN) +// For the interrupt signal, pick a signal number that: +// - is not otherwise used by mozilla or standard libraries +// - defaults to nostop and noprint on gdb/lldb so that noone is bothered +// SIGVTALRM a relative of SIGALRM, so intended for user code, but, unlike +// SIGALRM, not used anywhere else in Mozilla. +static const int sInterruptSignal = SIGVTALRM; + +static void +JitInterruptHandler(int signum, siginfo_t *info, void *context) +{ + if (JSRuntime *rt = RuntimeForCurrentThread()) + RedirectJitCodeToInterruptCheck(rt, (CONTEXT*)context); +} #endif bool -js::EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt) +js::EnsureSignalHandlersInstalled(JSRuntime *rt) { -#ifdef JS_CODEGEN_NONE - // Don't install signal handlers in builds with the JIT disabled. - return false; +#if defined(XP_MACOSX) + // On OSX, each JSRuntime gets its own handler thread. + if (!rt->asmJSMachExceptionHandler.installed() && !rt->asmJSMachExceptionHandler.install(rt)) + return false; #endif + // All the rest of the handlers are process-wide and thus must only be + // installed once. We assume that there are no races creating the first + // JSRuntime of the process. + static bool sTried = false; + static bool sResult = false; + if (sTried) + return sResult; + sTried = true; + +#if defined(ANDROID) + // Before Android 4.4 (SDK version 19), there is a bug + // https://android-review.googlesource.com/#/c/52333 + // in Bionic's pthread_join which causes pthread_join to return early when + // pthread_kill is used (on any thread). Nobody expects the pthread_cond_wait + // EINTRquisition. + char version_string[PROP_VALUE_MAX]; + PodArrayZero(version_string); + if (__system_property_get("ro.build.version.sdk", version_string) > 0) { + if (atol(version_string) < 19) + return false; + } +# if defined(MOZ_LINKER) + // Signal handling is broken on some android systems. if (IsSignalHandlingBroken()) return false; - -#if defined(XP_MACOSX) - // On OSX, each JSRuntime gets its own handler. - return rt->asmJSMachExceptionHandler.installed() || rt->asmJSMachExceptionHandler.install(rt); -#else - // Assume Windows or Unix. For these platforms, there is a single, - // process-wide signal handler installed. Take care to only install it once. - if (sInstalledHandlers) - return true; - -# if defined(XP_WIN) - if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler)) - return false; -# else - // Assume Unix. SA_NODEFER allows us to reenter the signal handler if we - // crash while handling the signal, and fall through to the Breakpad - // handler by testing handlingSignal. - struct sigaction sigAction; - sigAction.sa_flags = SA_SIGINFO | SA_NODEFER; - sigAction.sa_sigaction = &AsmJSFaultHandler; - sigemptyset(&sigAction.sa_mask); - if (sigaction(SIGSEGV, &sigAction, &sPrevHandler)) - return false; # endif - - sInstalledHandlers = true; #endif + +#if defined(XP_WIN) + // Windows uses SuspendThread to stop the main thread from another thread, + // so the only handler we need is for asm.js out-of-bound faults. + if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSFaultHandler)) + MOZ_CRASH("unable to install vectored exception handler"); +#else + // The interrupt handler allows the main thread to be paused from another + // thread (see InterruptRunningJitCode). + struct sigaction interruptHandler; + interruptHandler.sa_flags = SA_SIGINFO; + interruptHandler.sa_sigaction = &JitInterruptHandler; + sigemptyset(&interruptHandler.sa_mask); + struct sigaction prev; + if (sigaction(sInterruptSignal, &interruptHandler, &prev)) + MOZ_CRASH("unable to install interrupt handler"); + + // There shouldn't be any other handlers installed for sInterruptSignal. If + // there are, we could always forward, but we need to understand what we're + // doing to avoid problematic interference. + if ((prev.sa_flags & SA_SIGINFO && prev.sa_sigaction) || + (prev.sa_handler != SIG_DFL && prev.sa_handler != SIG_IGN)) + { + MOZ_CRASH("contention for interrupt signal"); + } + + // Lastly, install a SIGSEGV handler to handle safely-out-of-bounds asm.js + // heap access. OSX handles seg faults via the Mach exception handler above, + // so don't install AsmJSFaultHandler. +# if !defined(XP_MACOSX) + // SA_NODEFER allows us to reenter the signal handler if we crash while + // handling the signal, and fall through to the Breakpad handler by testing + // handlingSignal. + struct sigaction faultHandler; + faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER; + faultHandler.sa_sigaction = &AsmJSFaultHandler; + sigemptyset(&faultHandler.sa_mask); + if (sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler)) + MOZ_CRASH("unable to install segv handler"); +# endif // defined(XP_MACOSX) +#endif // defined(XP_WIN) + + sResult = true; return true; } -// To interrupt execution of a JSRuntime, any thread may call -// JS_RequestInterruptCallback (JSRuntime::requestInterruptCallback from inside -// the engine). In the simplest case, this sets some state that is polled at -// regular intervals (function prologues, loop headers). For tight loops, this -// poses non-trivial overhead. For asm.js, we can do better: when another -// thread requests an interrupt, we simply mprotect all of the innermost asm.js -// module activation's code. This will trigger a SIGSEGV, taking us into -// AsmJSFaultHandler. From there, we can manually redirect execution to call -// js::HandleExecutionInterrupt. The memory is un-protected from the signal -// handler after control flow is redirected. +// JSRuntime::requestInterrupt sets interrupt_ (which is checked frequently by +// C++ code at every Baseline JIT loop backedge) and jitStackLimit_ (which is +// checked at every Baseline and Ion JIT function prologue). The remaining +// sources of potential iloops (Ion loop backedges and all asm.js code) are +// handled by this function: +// 1. Ion loop backedges are patched to instead point to a stub that handles the +// interrupt; +// 2. if the main thread's pc is inside asm.js code, the pc is updated to point +// to a stub that handles the interrupt. void -js::RequestInterruptForAsmJSCode(JSRuntime *rt, int interruptModeRaw) +js::InterruptRunningJitCode(JSRuntime *rt) { - switch (JSRuntime::InterruptMode(interruptModeRaw)) { - case JSRuntime::RequestInterruptMainThread: - case JSRuntime::RequestInterruptAnyThread: - break; - case JSRuntime::RequestInterruptAnyThreadDontStopIon: - case JSRuntime::RequestInterruptAnyThreadForkJoin: - // It is ok to wait for asm.js execution to complete; we aren't trying - // to break an iloop or anything. Avoid the overhead of protecting all - // the code and taking a fault. + // If signal handlers weren't installed, then Ion and asm.js emit normal + // interrupt checks and don't need asynchronous interruption. + if (!rt->canUseSignalHandlers()) + return; + + // If we are on runtime's main thread, then: pc is not in asm.js code (so + // nothing to do for asm.js) and we can patch Ion backedges without any + // special synchronization. + if (rt == RuntimeForCurrentThread()) { + RedirectIonBackedgesToInterruptCheck(rt); return; } - AsmJSActivation *activation = rt->mainThread.asmJSActivationStack(); - if (!activation) - return; + // We are not on the runtime's main thread, so to do 1 and 2 above, we need + // to halt the runtime's main thread first. +#if defined(XP_WIN) + // On Windows, we can simply suspend the main thread and work directly on + // its context from this thread. + HANDLE thread = (HANDLE)rt->ownerThreadNative(); + if (SuspendThread(thread) == -1) + MOZ_CRASH("Failed to suspend main thread"); - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - activation->module().protectCode(rt); + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + if (!GetThreadContext(thread, &context)) + MOZ_CRASH("Failed to get suspended thread context"); + + RedirectJitCodeToInterruptCheck(rt, &context); + + if (!SetThreadContext(thread, &context)) + MOZ_CRASH("Failed to set suspended thread context"); + + if (ResumeThread(thread) == -1) + MOZ_CRASH("Failed to resume main thread"); +#else + // On Unix, we instead deliver an async signal to the main thread which + // halts the thread and callers our JitInterruptHandler (which has already + // been installed by EnsureSignalHandlersInstalled). + pthread_t thread = (pthread_t)rt->ownerThreadNative(); + pthread_kill(thread, sInterruptSignal); +#endif } // This is not supported by clang-cl yet. diff --git a/js/src/asmjs/AsmJSSignalHandlers.h b/js/src/asmjs/AsmJSSignalHandlers.h index d94ce1b611dd..e4133191d92b 100644 --- a/js/src/asmjs/AsmJSSignalHandlers.h +++ b/js/src/asmjs/AsmJSSignalHandlers.h @@ -28,15 +28,16 @@ struct JSRuntime; namespace js { -// Returns whether signal handlers for asm.js and for JitRuntime access -// violations have been installed. +// Set up any signal/exception handlers needed to execute code in the given +// runtime. Return whether runtime can: +// - rely on fault handler support for avoiding asm.js heap bounds checks +// - rely on InterruptRunningJitCode to halt running Ion/asm.js from any thread bool -EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt); +EnsureSignalHandlersInstalled(JSRuntime *rt); -// Force any currently-executing asm.js code to call -// js::HandleExecutionInterrupt. +// Force any currently-executing asm.js code to call HandleExecutionInterrupt. extern void -RequestInterruptForAsmJSCode(JSRuntime *rt, int interruptMode); +InterruptRunningJitCode(JSRuntime *rt); // On OSX we are forced to use the lower-level Mach exception mechanism instead // of Unix signals. Mach exceptions are not handled on the victim's stack but diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 9ceba1abcfb9..67006bf92988 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7624,20 +7624,6 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) return false; discardIonCode.ionScript = ionScript; - // Lock the runtime against interrupt callbacks during the link. - // We don't want an interrupt request to protect the code for the script - // before it has been filled in, as we could segv before the runtime's - // patchable backedges have been fully updated. - JSRuntime::AutoLockForInterrupt lock(cx->runtime()); - - // Make sure we don't segv while filling in the code, to avoid deadlocking - // inside the signal handler. - cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime()); - - // Implicit interrupts are used only for sequential code. In parallel mode - // use the normal executable allocator so that we cannot segv during - // execution off the main thread. - // // Also, note that creating the code here during an incremental GC will // trace the code and mark all GC things it refers to. This captures any // read barriers which were skipped while compiling the script off thread. diff --git a/js/src/jit/ExecutableAllocator.cpp b/js/src/jit/ExecutableAllocator.cpp index 6ae24b7f10ff..a6afe9618164 100644 --- a/js/src/jit/ExecutableAllocator.cpp +++ b/js/src/jit/ExecutableAllocator.cpp @@ -62,29 +62,3 @@ ExecutableAllocator::addSizeOfCode(JS::CodeSizes *sizes) const } } -void -ExecutableAllocator::toggleAllCodeAsAccessible(bool accessible) -{ - if (!m_pools.initialized()) - return; - - for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) { - ExecutablePool* pool = r.front(); - pool->toggleAllCodeAsAccessible(accessible); - } -} - -bool -ExecutableAllocator::codeContains(char* address) -{ - if (!m_pools.initialized()) - return false; - - for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) { - ExecutablePool* pool = r.front(); - if (pool->codeContains(address)) - return true; - } - - return false; -} diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h index 6268acc96bf3..aae98903557b 100644 --- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -170,12 +170,6 @@ private: MOZ_ASSERT(m_end >= m_freePtr); return m_end - m_freePtr; } - - void toggleAllCodeAsAccessible(bool accessible); - - bool codeContains(char* address) { - return address >= m_allocation.pages && address < m_freePtr; - } }; class ExecutableAllocator { @@ -260,8 +254,6 @@ public: } void addSizeOfCode(JS::CodeSizes *sizes) const; - void toggleAllCodeAsAccessible(bool accessible); - bool codeContains(char* address); void setDestroyCallback(DestroyCallback destroyCallback) { this->destroyCallback = destroyCallback; diff --git a/js/src/jit/ExecutableAllocatorPosix.cpp b/js/src/jit/ExecutableAllocatorPosix.cpp index 950667e325c1..6f79cd348178 100644 --- a/js/src/jit/ExecutableAllocatorPosix.cpp +++ b/js/src/jit/ExecutableAllocatorPosix.cpp @@ -91,18 +91,3 @@ void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSe } #endif -void -ExecutablePool::toggleAllCodeAsAccessible(bool accessible) -{ - char* begin = m_allocation.pages; - size_t size = m_freePtr - begin; - - if (size) { - // N.B. Some systems, like 32bit Mac OS 10.6, implicitly add PROT_EXEC - // when mprotect'ing memory with any flag other than PROT_NONE. Be - // sure to use PROT_NONE when making inaccessible. - int flags = accessible ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE; - if (mprotect(begin, size, flags)) - MOZ_CRASH(); - } -} diff --git a/js/src/jit/ExecutableAllocatorWin.cpp b/js/src/jit/ExecutableAllocatorWin.cpp index e18eb34b62c2..3c9add304d74 100644 --- a/js/src/jit/ExecutableAllocatorWin.cpp +++ b/js/src/jit/ExecutableAllocatorWin.cpp @@ -251,22 +251,6 @@ void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc) DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize); } -void -ExecutablePool::toggleAllCodeAsAccessible(bool accessible) -{ - char* begin = m_allocation.pages; - size_t size = m_freePtr - begin; - - if (size) { - // N.B. DEP is not on automatically in Windows XP, so be sure to use - // PAGE_NOACCESS instead of PAGE_READWRITE when making inaccessible. - DWORD oldProtect; - int flags = accessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS; - if (!VirtualProtect(begin, size, flags, &oldProtect)) - MOZ_CRASH(); - } -} - #if ENABLE_ASSEMBLER_WX_EXCLUSIVE #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform." #endif diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 0ea6001111d8..c820f55d7b55 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -167,7 +167,7 @@ JitRuntime::JitRuntime() baselineDebugModeOSRHandler_(nullptr), functionWrappers_(nullptr), osrTempData_(nullptr), - ionCodeProtected_(false), + mutatingBackedgeList_(false), ionReturnOverride_(MagicValue(JS_ARG_POISON)), jitcodeGlobalTable_(nullptr) { @@ -191,7 +191,6 @@ bool JitRuntime::initialize(JSContext *cx) { MOZ_ASSERT(cx->runtime()->currentThreadHasExclusiveAccess()); - MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock()); AutoCompartment ac(cx, cx->atomsCompartment()); @@ -366,85 +365,17 @@ JitRuntime::freeOsrTempData() ExecutableAllocator * JitRuntime::createIonAlloc(JSContext *cx) { - MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock()); - ionAlloc_ = js_new(); if (!ionAlloc_) js_ReportOutOfMemory(cx); return ionAlloc_; } -void -JitRuntime::ensureIonCodeProtected(JSRuntime *rt) -{ - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - - if (!rt->signalHandlersInstalled() || ionCodeProtected_ || !ionAlloc_) - return; - - // Protect all Ion code in the runtime to trigger an access violation the - // next time any of it runs on the main thread. - ionAlloc_->toggleAllCodeAsAccessible(false); - ionCodeProtected_ = true; -} - -bool -JitRuntime::handleAccessViolation(JSRuntime *rt, void *faultingAddress) -{ - if (!rt->signalHandlersInstalled() || !ionAlloc_ || !ionAlloc_->codeContains((char *) faultingAddress)) - return false; - - // All places where the interrupt lock is taken must either ensure that Ion - // code memory won't be accessed within, or call ensureIonCodeAccessible to - // render the memory safe for accessing. Otherwise taking the lock below - // will deadlock the process. - MOZ_ASSERT(!rt->currentThreadOwnsInterruptLock()); - - // Taking this lock is necessary to prevent the interrupting thread from marking - // the memory as inaccessible while we are patching backedges. This will cause us - // to SEGV while still inside the signal handler, and the process will terminate. - JSRuntime::AutoLockForInterrupt lock(rt); - - // Ion code in the runtime faulted after it was made inaccessible. Reset - // the code privileges and patch all loop backedges to perform an interrupt - // check instead. - ensureIonCodeAccessible(rt); - return true; -} - -void -JitRuntime::ensureIonCodeAccessible(JSRuntime *rt) -{ - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - - // This can only be called on the main thread and while handling signals, - // which happens on a separate thread in OS X. -#ifndef XP_MACOSX - MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); -#endif - - if (ionCodeProtected_) { - ionAlloc_->toggleAllCodeAsAccessible(true); - ionCodeProtected_ = false; - } - - if (rt->hasPendingInterrupt()) { - // The interrupt handler needs to be invoked by this thread, but we may - // be inside a signal handler and have no idea what is above us on the - // stack (probably we are executing Ion code at an arbitrary point, but - // we could be elsewhere, say repatching a jump for an IonCache). - // Patch all backedges in the runtime so they will invoke the interrupt - // handler the next time they execute. - patchIonBackedges(rt, BackedgeInterruptCheck); - } -} - void JitRuntime::patchIonBackedges(JSRuntime *rt, BackedgeTarget target) { -#ifndef XP_MACOSX - MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); -#endif + MOZ_ASSERT_IF(target == BackedgeLoopHeader, mutatingBackedgeList_); + MOZ_ASSERT_IF(target == BackedgeInterruptCheck, !mutatingBackedgeList_); // Patch all loop backedges in Ion code so that they either jump to the // normal loop header or to an interrupt handler each time they run. @@ -460,47 +391,6 @@ JitRuntime::patchIonBackedges(JSRuntime *rt, BackedgeTarget target) } } -void -jit::RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode) -{ - JitRuntime *jitRuntime = rt->jitRuntime(); - if (!jitRuntime) - return; - - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - - // The mechanism for interrupting normal ion code varies depending on how - // the interrupt is being requested. - switch (mode) { - case JSRuntime::RequestInterruptMainThread: - // When requesting an interrupt from the main thread, Ion loop - // backedges can be patched directly. Make sure we don't segv while - // patching the backedges, to avoid deadlocking inside the signal - // handler. - MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); - jitRuntime->ensureIonCodeAccessible(rt); - break; - - case JSRuntime::RequestInterruptAnyThread: - // When requesting an interrupt from off the main thread, protect - // Ion code memory so that the main thread will fault and enter a - // signal handler when trying to execute the code. The signal - // handler will unprotect the code and patch loop backedges so - // that the interrupt handler is invoked afterwards. - jitRuntime->ensureIonCodeProtected(rt); - break; - - case JSRuntime::RequestInterruptAnyThreadDontStopIon: - case JSRuntime::RequestInterruptAnyThreadForkJoin: - // The caller does not require Ion code to be interrupted. - // Nothing more needs to be done. - break; - - default: - MOZ_CRASH("Bad interrupt mode"); - } -} - JitCompartment::JitCompartment() : stubCodes_(nullptr), baselineCallReturnAddr_(nullptr), @@ -870,10 +760,6 @@ JitCode::trace(JSTracer *trc) void JitCode::finalize(FreeOp *fop) { - // Make sure this can't race with an interrupting thread, which may try - // to read the contents of the pool we are releasing references in. - MOZ_ASSERT(fop->runtime()->currentThreadOwnsInterruptLock()); - // If this jitcode has a bytecode map, de-register it. if (hasBytecodeMap_) { MOZ_ASSERT(fop->runtime()->jitRuntime()->hasJitcodeGlobalTable()); @@ -883,7 +769,7 @@ JitCode::finalize(FreeOp *fop) // Buffer can be freed at any time hereafter. Catch use-after-free bugs. // Don't do this if the Ion code is protected, as the signal handler will // deadlock trying to reacquire the interrupt lock. - if (fop->runtime()->jitRuntime() && !fop->runtime()->jitRuntime()->ionCodeProtected()) + if (fop->runtime()->jitRuntime()) memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_); code_ = nullptr; @@ -1144,6 +1030,9 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code, PatchableBackedgeInfo *backedges, MacroAssembler &masm) { + JitRuntime *jrt = cx->runtime()->jitRuntime(); + JitRuntime::AutoMutateBackedges amb(jrt); + for (size_t i = 0; i < backedgeEntries_; i++) { PatchableBackedgeInfo &info = backedges[i]; PatchableBackedge *patchableBackedge = &backedgeList()[i]; @@ -1167,7 +1056,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code, else PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader); - cx->runtime()->jitRuntime()->addPatchableBackedge(patchableBackedge); + jrt->addPatchableBackedge(patchableBackedge); } } @@ -1358,11 +1247,10 @@ IonScript::unlinkFromRuntime(FreeOp *fop) // The writes to the executable buffer below may clobber backedge jumps, so // make sure that those backedges are unlinked from the runtime and not // reclobbered with garbage if an interrupt is requested. - JSRuntime *rt = fop->runtime(); - for (size_t i = 0; i < backedgeEntries_; i++) { - PatchableBackedge *backedge = &backedgeList()[i]; - rt->jitRuntime()->removePatchableBackedge(backedge); - } + JitRuntime *jrt = fop->runtime()->jitRuntime(); + JitRuntime::AutoMutateBackedges amb(jrt); + for (size_t i = 0; i < backedgeEntries_; i++) + jrt->removePatchableBackedge(&backedgeList()[i]); // Clear the list of backedges, so that this method is idempotent. It is // called during destruction, and may be additionally called when the diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index 30d4daf06fd5..95093821b59a 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -201,8 +201,6 @@ size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf); void DestroyIonScripts(FreeOp *fop, JSScript *script); void TraceIonScripts(JSTracer* trc, JSScript *script); -void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode); - bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp); bool UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp, AutoDebugModeInvalidation &invalidate); diff --git a/js/src/jit/IonLinker.h b/js/src/jit/IonLinker.h index de97815f8f48..fa63b7ce046f 100644 --- a/js/src/jit/IonLinker.h +++ b/js/src/jit/IonLinker.h @@ -82,10 +82,6 @@ class Linker } JitCode *newCodeForIonScript(JSContext *cx) { - // The caller must lock the runtime against interrupt requests, as the - // thread requesting an interrupt may use the executable allocator below. - MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock()); - ExecutableAllocator *alloc = cx->runtime()->jitRuntime()->getIonAlloc(cx); if (!alloc) return nullptr; diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index 718373896884..a76999230068 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -217,12 +217,11 @@ class JitRuntime // (after returning from JIT code). uint8_t *osrTempData_; - // Whether all Ion code in the runtime is protected, and will fault if it - // is accessed. - bool ionCodeProtected_; - - // If signal handlers are installed, this contains all loop backedges for - // IonScripts in the runtime. + // List of all backedges in all Ion code. The backedge edge list is accessed + // asynchronously when the main thread is paused and mutatingBackedgeList_ + // is false. Thus, the list must only be mutated while mutatingBackedgeList_ + // is true. + volatile bool mutatingBackedgeList_; InlineList backedgeList_; // In certain cases, we want to optimize certain opcodes to typed instructions, @@ -274,29 +273,39 @@ class JitRuntime ExecutableAllocator *execAlloc() const { return execAlloc_; } - ExecutableAllocator *getIonAlloc(JSContext *cx) { - MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock()); return ionAlloc_ ? ionAlloc_ : createIonAlloc(cx); } - ExecutableAllocator *ionAlloc(JSRuntime *rt) { - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); return ionAlloc_; } - bool hasIonAlloc() const { return !!ionAlloc_; } - bool ionCodeProtected() { - return ionCodeProtected_; - } + class AutoMutateBackedges + { + JitRuntime *jrt_; + public: + AutoMutateBackedges(JitRuntime *jrt) : jrt_(jrt) { + MOZ_ASSERT(!jrt->mutatingBackedgeList_); + jrt->mutatingBackedgeList_ = true; + } + ~AutoMutateBackedges() { + MOZ_ASSERT(jrt_->mutatingBackedgeList_); + jrt_->mutatingBackedgeList_ = false; + } + }; + bool mutatingBackedgeList() const { + return mutatingBackedgeList_; + } void addPatchableBackedge(PatchableBackedge *backedge) { + MOZ_ASSERT(mutatingBackedgeList_); backedgeList_.pushFront(backedge); } void removePatchableBackedge(PatchableBackedge *backedge) { + MOZ_ASSERT(mutatingBackedgeList_); backedgeList_.remove(backedge); } @@ -305,12 +314,8 @@ class JitRuntime BackedgeInterruptCheck }; - void ensureIonCodeProtected(JSRuntime *rt); - void ensureIonCodeAccessible(JSRuntime *rt); void patchIonBackedges(JSRuntime *rt, BackedgeTarget target); - bool handleAccessViolation(JSRuntime *rt, void *faultingAddress); - JitCode *getVMWrapper(const VMFunction &f) const; JitCode *debugTrapHandler(JSContext *cx); JitCode *getBaselineDebugModeOSRHandler(JSContext *cx); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 8b0b2636f1e3..b3559aff62e5 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -522,15 +522,11 @@ InterruptCheck(JSContext *cx) { gc::MaybeVerifyBarriers(cx); - // Fix loop backedges so that they do not invoke the interrupt again. - // No lock is held here and it's possible we could segv in the middle here - // and end up with a state where some fraction of the backedges point to - // the interrupt handler and some don't. This is ok since the interrupt - // is definitely about to be handled; if there are still backedges - // afterwards which point to the interrupt handler, the next time they are - // taken the backedges will just be reset again. - cx->runtime()->jitRuntime()->patchIonBackedges(cx->runtime(), - JitRuntime::BackedgeLoopHeader); + { + JitRuntime *jrt = cx->runtime()->jitRuntime(); + JitRuntime::AutoMutateBackedges amb(jrt); + jrt->patchIonBackedges(cx->runtime(), JitRuntime::BackedgeLoopHeader); + } return CheckForInterrupt(cx); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index cdb7b362dbd5..aaef5c88088e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5107,7 +5107,7 @@ JS_GetInterruptCallback(JSRuntime *rt) JS_PUBLIC_API(void) JS_RequestInterruptCallback(JSRuntime *rt) { - rt->requestInterrupt(JSRuntime::RequestInterruptAnyThread); + rt->requestInterrupt(JSRuntime::RequestInterruptUrgent); } JS_PUBLIC_API(bool) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4c81f3febbd0..9f9dd119dbcd 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -293,7 +293,6 @@ struct ThreadSafeContext : ContextFriendFields, void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } void *stackLimitAddressForJitCode(StackKind kind); size_t gcSystemPageSize() { return gc::SystemPageSize(); } - bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); } bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); } bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 826e42167da2..fd54b3b5f8bc 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -33,6 +33,7 @@ using namespace js; using namespace js::gc; +using namespace js::jit; using mozilla::DebugOnly; @@ -125,18 +126,18 @@ JSRuntime::createJitRuntime(JSContext *cx) // accessed by other threads with an exclusive context. AutoLockForExclusiveAccess atomsLock(cx); - // The runtime will only be created on its owning thread, but reads of a - // runtime's jitRuntime() can occur when another thread is requesting an - // interrupt. - AutoLockForInterrupt lock(this); - MOZ_ASSERT(!jitRuntime_); - jitRuntime_ = cx->new_(); - - if (!jitRuntime_) + jit::JitRuntime *jrt = cx->new_(); + if (!jrt) return nullptr; + // Protect jitRuntime_ from being observed (by InterruptRunningJitCode) + // while it is being initialized. Unfortunately, initialization depends on + // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_. + JitRuntime::AutoMutateBackedges amb(jrt); + jitRuntime_ = jrt; + if (!jitRuntime_->initialize(cx)) { js_delete(jitRuntime_); jitRuntime_ = nullptr; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index e4e97d2b3360..cc914ecafcd3 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -645,12 +645,7 @@ FinalizeArenas(FreeOp *fop, case FINALIZE_SYMBOL: return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_JITCODE: - { - // JitCode finalization may release references on an executable - // allocator that is accessed when requesting interrupts. - JSRuntime::AutoLockForInterrupt lock(fop->runtime()); return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); - } default: MOZ_CRASH("Invalid alloc kind"); } @@ -1063,7 +1058,7 @@ class js::gc::AutoMaybeStartBackgroundAllocation } ~AutoMaybeStartBackgroundAllocation() { - if (runtime && !runtime->currentThreadOwnsInterruptLock()) + if (runtime) runtime->gc.startBackgroundAllocTaskIfIdle(); } }; @@ -3000,7 +2995,7 @@ GCRuntime::requestMajorGC(JS::gcreason::Reason reason) majorGCRequested = true; majorGCTriggerReason = reason; - rt->requestInterrupt(JSRuntime::RequestInterruptMainThread); + rt->requestInterrupt(JSRuntime::RequestInterruptUrgent); } void @@ -3012,7 +3007,7 @@ GCRuntime::requestMinorGC(JS::gcreason::Reason reason) minorGCRequested = true; minorGCTriggerReason = reason; - rt->requestInterrupt(JSRuntime::RequestInterruptMainThread); + rt->requestInterrupt(JSRuntime::RequestInterruptUrgent); } bool @@ -3031,10 +3026,6 @@ GCRuntime::triggerGC(JS::gcreason::Reason reason) if (!CurrentThreadCanAccessRuntime(rt)) return false; - /* Don't trigger GCs when allocating under the interrupt callback lock. */ - if (rt->currentThreadOwnsInterruptLock()) - return false; - /* GC is already running. */ if (rt->isHeapCollecting()) return false; @@ -3094,10 +3085,6 @@ GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason) if (zone->usedByExclusiveThread) return false; - /* Don't trigger GCs when allocating under the interrupt callback lock. */ - if (rt->currentThreadOwnsInterruptLock()) - return false; - /* GC is already running. */ if (rt->isHeapCollecting()) return false; @@ -5344,10 +5331,8 @@ GCRuntime::endSweepPhase(bool lastGC) if (jit::ExecutableAllocator *execAlloc = rt->maybeExecAlloc()) execAlloc->purge(); - if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc()) { - JSRuntime::AutoLockForInterrupt lock(rt); + if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc()) rt->jitRuntime()->ionAlloc(rt)->purge(); - } /* * This removes compartments from rt->compartment, so we do it last to make diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 7e1471311716..529e510f1510 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -1667,9 +1667,7 @@ ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal) abort_ = true; fatal_ = fatal_ || fatal; - // Note: The ForkJoin trigger here avoids the expensive memory protection needed to - // interrupt Ion code compiled for sequential execution. - cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptAnyThreadForkJoin); + cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptCanWait); } void diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 37cfea2863a3..a4a27d2b4ac4 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1065,7 +1065,7 @@ HelperThread::handleIonWorkload() // at the next interrupt callback. Don't interrupt Ion code for this, as // this incorporation can be delayed indefinitely without affecting // performance as long as the main thread is actually executing Ion code. - rt->requestInterrupt(JSRuntime::RequestInterruptAnyThreadDontStopIon); + rt->requestInterrupt(JSRuntime::RequestInterruptCanWait); // Notify the main thread in case it is waiting for the compilation to finish. HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 44b5516380e5..66acf33b3a0e 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -27,6 +27,7 @@ #include "jsobj.h" #include "jsscript.h" #include "jswatchpoint.h" +#include "jswin.h" #include "jswrapper.h" #include "asmjs/AsmJSSignalHandlers.h" @@ -140,8 +141,6 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) interruptPar_(false), handlingSignal(false), interruptCallback(nullptr), - interruptLock(nullptr), - interruptLockOwner(nullptr), exclusiveAccessLock(nullptr), exclusiveAccessOwner(nullptr), mainThreadHasExclusiveAccess(false), @@ -152,6 +151,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) defaultVersion_(JSVERSION_DEFAULT), futexAPI_(nullptr), ownerThread_(nullptr), + ownerThreadNative_(0), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc_(nullptr), jitRuntime_(nullptr), @@ -256,9 +256,19 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) { ownerThread_ = PR_GetCurrentThread(); - interruptLock = PR_NewLock(); - if (!interruptLock) - return false; + // Get a platform-native handle for the owner thread, used by + // js::InterruptRunningJitCode to halt the runtime's main thread. +#ifdef XP_WIN + size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME; + HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId()); + if (!self) + MOZ_CRASH("Unable to open thread handle"); + static_assert(sizeof(HANDLE) <= sizeof(ownerThreadNative_), "need bigger field"); + ownerThreadNative_ = (size_t)self; +#else + static_assert(sizeof(pthread_t) <= sizeof(ownerThreadNative_), "need bigger field"); + ownerThreadNative_ = (size_t)pthread_self(); +#endif exclusiveAccessLock = PR_NewLock(); if (!exclusiveAccessLock) @@ -325,7 +335,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint(); jitSupportsSimd = js::jit::JitSupportsSimd(); - signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this); + signalHandlersInstalled_ = EnsureSignalHandlersInstalled(this); canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled(); if (!spsProfiler.init()) @@ -391,10 +401,6 @@ JSRuntime::~JSRuntime() MOZ_ASSERT(!numExclusiveThreads); mainThreadHasExclusiveAccess = true; - MOZ_ASSERT(!interruptLockOwner); - if (interruptLock) - PR_DestroyLock(interruptLock); - /* * Even though all objects in the compartment are dead, we may have keep * some filenames around because of gcKeepAtoms. @@ -444,6 +450,11 @@ JSRuntime::~JSRuntime() MOZ_ASSERT(oldCount > 0); js::TlsPerThreadData.set(nullptr); + +#ifdef XP_WIN + if (ownerThreadNative_) + CloseHandle((HANDLE)ownerThreadNative_); +#endif } void @@ -500,13 +511,9 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim if (execAlloc_) execAlloc_->addSizeOfCode(&rtSizes->code); - { - AutoLockForInterrupt lock(this); - if (jitRuntime()) { - if (jit::ExecutableAllocator *ionAlloc = jitRuntime()->ionAlloc(this)) - ionAlloc->addSizeOfCode(&rtSizes->code); - } - } + + if (jitRuntime() && jitRuntime()->ionAlloc(this)) + jitRuntime()->ionAlloc(this)->addSizeOfCode(&rtSizes->code); rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf); #ifdef JSGC_GENERATIONAL @@ -611,11 +618,8 @@ JSRuntime::requestInterrupt(InterruptMode mode) interruptPar_ = true; mainThread.jitStackLimit_ = UINTPTR_MAX; - if (canUseSignalHandlers()) { - AutoLockForInterrupt lock(this); - RequestInterruptForAsmJSCode(this, mode); - jit::RequestInterruptForIonCode(this, mode); - } + if (mode == JSRuntime::RequestInterruptUrgent) + InterruptRunningJitCode(this); } bool @@ -836,8 +840,6 @@ JSRuntime::assertCanLock(RuntimeLock which) MOZ_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread()); case HelperThreadStateLock: MOZ_ASSERT(!HelperThreadState().isLocked()); - case InterruptLock: - MOZ_ASSERT(!currentThreadOwnsInterruptLock()); case GCLock: gc.assertCanLock(); break; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 3735e3140fec..204fa3bc6c1c 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -452,7 +452,6 @@ AtomStateOffsetToName(const JSAtomState &atomState, size_t offset) enum RuntimeLock { ExclusiveAccessLock, HelperThreadStateLock, - InterruptLock, GCLock }; @@ -541,14 +540,6 @@ class PerThreadData : public PerThreadDataFriendFields TraceLogger *traceLogger; #endif - /* - * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This - * stack is used by JSRuntime::requestInterrupt to stop long-running asm.js - * without requiring dynamic polling operations in the generated - * code. Since requestInterrupt may run on a separate thread than the - * JSRuntime's owner thread all reads/writes must be synchronized (by - * rt->interruptLock). - */ private: friend class js::Activation; friend class js::ActivationIterator; @@ -566,11 +557,11 @@ class PerThreadData : public PerThreadDataFriendFields /* * Points to the most recent profiling activation running on the - * thread. Protected by rt->interruptLock. + * thread. */ js::Activation * volatile profilingActivation_; - /* See AsmJSActivation comment. Protected by rt->interruptLock. */ + /* See AsmJSActivation comment. */ js::AsmJSActivation * volatile asmJSActivationStack_; /* Pointer to the current AutoFlushICache. */ @@ -714,10 +705,8 @@ struct JSRuntime : public JS::shadow::Runtime, public: enum InterruptMode { - RequestInterruptMainThread, - RequestInterruptAnyThread, - RequestInterruptAnyThreadDontStopIon, - RequestInterruptAnyThreadForkJoin + RequestInterruptUrgent, + RequestInterruptCanWait }; // Any thread can call requestInterrupt() to request that the main JS thread @@ -770,37 +759,6 @@ struct JSRuntime : public JS::shadow::Runtime, void assertCanLock(js::RuntimeLock which) {} #endif - private: - /* - * Lock taken when triggering an interrupt from another thread. - * Protects all data that is touched in this process. - */ - PRLock *interruptLock; - PRThread *interruptLockOwner; - - public: - class AutoLockForInterrupt { - JSRuntime *rt; - public: - explicit AutoLockForInterrupt(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - rt->assertCanLock(js::InterruptLock); - PR_Lock(rt->interruptLock); - rt->interruptLockOwner = PR_GetCurrentThread(); - } - ~AutoLockForInterrupt() { - MOZ_ASSERT(rt->currentThreadOwnsInterruptLock()); - rt->interruptLockOwner = nullptr; - PR_Unlock(rt->interruptLock); - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - }; - - bool currentThreadOwnsInterruptLock() { - return interruptLockOwner == PR_GetCurrentThread(); - } - private: /* * Lock taken when using per-runtime or per-zone data that could otherwise @@ -852,9 +810,14 @@ struct JSRuntime : public JS::shadow::Runtime, private: /* See comment for JS_AbortIfWrongThread in jsapi.h. */ void *ownerThread_; + size_t ownerThreadNative_; friend bool js::CurrentThreadCanAccessRuntime(JSRuntime *rt); public: + size_t ownerThreadNative() const { + return ownerThreadNative_; + } + /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; js::LifoAlloc tempLifoAlloc; @@ -1089,16 +1052,15 @@ struct JSRuntime : public JS::shadow::Runtime, #endif private: - // Whether asm.js signal handlers have been installed and can be used for - // performing interrupt checks in loops. + // Whether EnsureSignalHandlersInstalled succeeded in installing all the + // relevant handlers for this platform. bool signalHandlersInstalled_; + // Whether we should use them or they have been disabled for making // debugging easier. If signal handlers aren't installed, it is set to false. bool canUseSignalHandlers_; + public: - bool signalHandlersInstalled() const { - return signalHandlersInstalled_; - } bool canUseSignalHandlers() const { return canUseSignalHandlers_; } diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index c57083d1732e..7700024ada28 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1558,11 +1558,7 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) module.activation() = this; prevAsmJS_ = cx->mainThread().asmJSActivationStack_; - - { - JSRuntime::AutoLockForInterrupt lock(cx->runtime()); - cx->mainThread().asmJSActivationStack_ = this; - } + cx->mainThread().asmJSActivationStack_ = this; // Now that the AsmJSActivation is fully initialized, make it visible to // asynchronous profiling. @@ -1585,7 +1581,6 @@ AsmJSActivation::~AsmJSActivation() JSContext *cx = cx_->asJSContext(); MOZ_ASSERT(cx->mainThread().asmJSActivationStack_ == this); - JSRuntime::AutoLockForInterrupt lock(cx->runtime()); cx->mainThread().asmJSActivationStack_ = prevAsmJS_; } @@ -1609,7 +1604,6 @@ void Activation::registerProfiling() { MOZ_ASSERT(isProfiling()); - JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime()); cx_->perThreadData->profilingActivation_ = this; } @@ -1617,7 +1611,6 @@ void Activation::unregisterProfiling() { MOZ_ASSERT(isProfiling()); - JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime()); MOZ_ASSERT(cx_->perThreadData->profilingActivation_ == this); cx_->perThreadData->profilingActivation_ = prevProfiling_; }