diff --git a/js/src/ds/MemoryProtectionExceptionHandler.cpp b/js/src/ds/MemoryProtectionExceptionHandler.cpp deleted file mode 100644 index 2b6261dd9d04..000000000000 --- a/js/src/ds/MemoryProtectionExceptionHandler.cpp +++ /dev/null @@ -1,753 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ds/MemoryProtectionExceptionHandler.h" - -#include "mozilla/Assertions.h" -#include "mozilla/Atomics.h" - -#if defined(XP_WIN) -# include "jswin.h" -#elif defined(XP_LINUX) -# include -# include -# include -#elif defined(XP_DARWIN) -# include -#endif - -#ifdef ANDROID -# include -#endif - -#include "ds/SplayTree.h" - -#include "threading/LockGuard.h" -#include "threading/Mutex.h" - -namespace js { - -/* - * A class to store the addresses of the regions recognized as protected - * by this exception handler. We use a splay tree to store these addresses. - */ -class ProtectedRegionTree -{ - struct Region - { - uintptr_t first; - uintptr_t last; - - Region(uintptr_t addr, size_t size) : first(addr), - last(addr + (size - 1)) {} - - static int compare(const Region& A, const Region& B) { - if (A.last < B.first) - return -1; - if (A.first > B.last) - return 1; - return 0; - } - }; - - Mutex lock; - LifoAlloc alloc; - SplayTree tree; - - public: - ProtectedRegionTree() : alloc(4096), - tree(&alloc) {} - - ~ProtectedRegionTree() { MOZ_ASSERT(tree.empty()); } - - void insert(uintptr_t addr, size_t size) { - MOZ_ASSERT(addr && size); - LockGuard guard(lock); - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!tree.insert(Region(addr, size))) - oomUnsafe.crash("Failed to store allocation ID."); - } - - void remove(uintptr_t addr) { - MOZ_ASSERT(addr); - LockGuard guard(lock); - tree.remove(Region(addr, 1)); - } - - bool isProtected(uintptr_t addr) { - if (!addr) - return false; - LockGuard guard(lock); - return tree.maybeLookup(Region(addr, 1)); - } -}; - -static bool sExceptionHandlerInstalled = false; - -static ProtectedRegionTree sProtectedRegions; - -bool -MemoryProtectionExceptionHandler::isDisabled() -{ - // There isn't currently any reason to disable it. - return false; -} - -void -MemoryProtectionExceptionHandler::addRegion(void* addr, size_t size) -{ - if (sExceptionHandlerInstalled) - sProtectedRegions.insert(uintptr_t(addr), size); -} - -void -MemoryProtectionExceptionHandler::removeRegion(void* addr) -{ - if (sExceptionHandlerInstalled) - sProtectedRegions.remove(uintptr_t(addr)); -} - -/* -------------------------------------------------------------------------- */ - -/* - * This helper function attempts to replicate the functionality of - * mozilla::MOZ_ReportCrash() in an async-signal-safe way. - */ -static MOZ_COLD MOZ_ALWAYS_INLINE void -ReportCrashIfDebug(const char* aStr) - MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS -{ -#ifdef DEBUG -# if defined(XP_WIN) - DWORD bytesWritten; - BOOL ret = WriteFile(GetStdHandle(STD_ERROR_HANDLE), aStr, - strlen(aStr) + 1, &bytesWritten, nullptr); - ::__debugbreak(); -# elif defined(ANDROID) - int ret = __android_log_write(ANDROID_LOG_FATAL, "MOZ_CRASH", aStr); -# else - ssize_t ret = write(STDERR_FILENO, aStr, strlen(aStr) + 1); -# endif - (void)ret; // Ignore failures; we're already crashing anyway. -#endif -} - -/* -------------------------------------------------------------------------- */ - -#if defined(XP_WIN) - -static void* sVectoredExceptionHandler = nullptr; - -/* - * We can only handle one exception. To guard against races and reentrancy, - * we set this value the first time we enter the exception handler and never - * touch it again. - */ -static mozilla::Atomic sHandlingException(false); - -static long __stdcall -VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) -{ - EXCEPTION_RECORD* ExceptionRecord = ExceptionInfo->ExceptionRecord; - - // We only handle one kind of exception; ignore all others. - if (ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - // Make absolutely sure we can only get here once. - if (sHandlingException.compareExchange(false, true)) { - // Restore the previous handler. We're going to forward to it - // anyway, and if we crash while doing so we don't want to hang. - MOZ_ALWAYS_TRUE(RemoveVectoredExceptionHandler(sVectoredExceptionHandler)); - - // Get the address that the offending code tried to access. - uintptr_t address = uintptr_t(ExceptionRecord->ExceptionInformation[1]); - - // If the faulting address is in one of our protected regions, we - // want to annotate the crash to make it stand out from the crowd. - if (sProtectedRegions.isProtected(address)) { - ReportCrashIfDebug("Hit MOZ_CRASH(Tried to access a protected region!)\n"); - MOZ_CRASH_ANNOTATE("Tried to access a protected region!"); - } - } - } - - // Forward to the previous handler which may be a debugger, - // the crash reporter or something else entirely. - return EXCEPTION_CONTINUE_SEARCH; -} - -bool -MemoryProtectionExceptionHandler::install() -{ - MOZ_ASSERT(!sExceptionHandlerInstalled); - - // If the exception handler is disabled, report success anyway. - if (MemoryProtectionExceptionHandler::isDisabled()) - return true; - - // Install our new exception handler. - sVectoredExceptionHandler = AddVectoredExceptionHandler(/* FirstHandler = */ true, - VectoredExceptionHandler); - - sExceptionHandlerInstalled = sVectoredExceptionHandler != nullptr; - return sExceptionHandlerInstalled; -} - -void -MemoryProtectionExceptionHandler::uninstall() -{ - if (sExceptionHandlerInstalled) { - MOZ_ASSERT(!sHandlingException); - - // Restore the previous exception handler. - MOZ_ALWAYS_TRUE(RemoveVectoredExceptionHandler(sVectoredExceptionHandler)); - - sExceptionHandlerInstalled = false; - } -} - -#elif defined(XP_LINUX) - -static struct sigaction sPrevSEGVHandler = {}; - -/* - * We can only handle one exception. To guard against races and reentrancy, - * we set this value the first time we enter the exception handler and never - * touch it again. - */ -static mozilla::Atomic sHandlingException(false); - -static void -UnixExceptionHandler(int signum, siginfo_t* info, void* context) -{ - // Make absolutely sure we can only get here once. - if (sHandlingException.compareExchange(false, true)) { - // Restore the previous handler. We're going to forward to it - // anyway, and if we crash while doing so we don't want to hang. - MOZ_ALWAYS_FALSE(sigaction(SIGSEGV, &sPrevSEGVHandler, nullptr)); - - MOZ_ASSERT(signum == SIGSEGV && info->si_signo == SIGSEGV); - - if (info->si_code == SEGV_ACCERR) { - // Get the address that the offending code tried to access. - uintptr_t address = uintptr_t(info->si_addr); - - // If the faulting address is in one of our protected regions, we - // want to annotate the crash to make it stand out from the crowd. - if (sProtectedRegions.isProtected(address)) { - ReportCrashIfDebug("Hit MOZ_CRASH(Tried to access a protected region!)\n"); - MOZ_CRASH_ANNOTATE("Tried to access a protected region!"); - } - } - } - - // Forward to the previous handler which may be a debugger, - // the crash reporter or something else entirely. - if (sPrevSEGVHandler.sa_flags & SA_SIGINFO) - sPrevSEGVHandler.sa_sigaction(signum, info, context); - else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN) - raise(signum); - else - sPrevSEGVHandler.sa_handler(signum); -} - -bool -MemoryProtectionExceptionHandler::install() -{ - MOZ_ASSERT(!sExceptionHandlerInstalled); - - // If the exception handler is disabled, report success anyway. - if (MemoryProtectionExceptionHandler::isDisabled()) - return true; - - // Install our new exception handler and save the previous one. - struct sigaction faultHandler = {}; - faultHandler.sa_flags = SA_SIGINFO; - faultHandler.sa_sigaction = UnixExceptionHandler; - sigemptyset(&faultHandler.sa_mask); - sExceptionHandlerInstalled = !sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler); - - return sExceptionHandlerInstalled; -} - -void -MemoryProtectionExceptionHandler::uninstall() -{ - if (sExceptionHandlerInstalled) { - MOZ_ASSERT(!sHandlingException); - - // Restore the previous exception handler. - MOZ_ALWAYS_FALSE(sigaction(SIGSEGV, &sPrevSEGVHandler, nullptr)); - - sExceptionHandlerInstalled = false; - } -} - -#elif defined(XP_DARWIN) - -/* - * The fact that we need to be able to forward to other exception handlers - * makes this code much more complicated. The forwarding logic and the - * structures required to make it work are heavily based on the code at - * www.ravenbrook.com/project/mps/prototype/2013-06-24/machtest/machtest/main.c - */ - -/* -------------------------------------------------------------------------- */ -/* Begin Mach definitions and helper functions */ -/* -------------------------------------------------------------------------- */ - -/* - * These are the message IDs associated with each exception type. - * We'll be using sIDRequest64, but we need the others for forwarding. - */ -static const mach_msg_id_t sIDRequest32 = 2401; -static const mach_msg_id_t sIDRequestState32 = 2402; -static const mach_msg_id_t sIDRequestStateIdentity32 = 2403; - -static const mach_msg_id_t sIDRequest64 = 2405; -static const mach_msg_id_t sIDRequestState64 = 2406; -static const mach_msg_id_t sIDRequestStateIdentity64 = 2407; - -/* - * Each message ID has an associated Mach message structure. - * We use the preprocessor to make defining them a little less arduous. - */ -# define REQUEST_HEADER_FIELDS\ - mach_msg_header_t header; - -# define REQUEST_IDENTITY_FIELDS\ - mach_msg_body_t msgh_body;\ - mach_msg_port_descriptor_t thread;\ - mach_msg_port_descriptor_t task; - -# define REQUEST_GENERAL_FIELDS(bits)\ - NDR_record_t NDR;\ - exception_type_t exception;\ - mach_msg_type_number_t code_count;\ - int##bits##_t code[2]; - -# define REQUEST_STATE_FIELDS\ - int flavor;\ - mach_msg_type_number_t old_state_count;\ - natural_t old_state[THREAD_STATE_MAX]; - -# define REQUEST_TRAILER_FIELDS\ - mach_msg_trailer_t trailer; - -# define EXCEPTION_REQUEST(bits)\ - struct ExceptionRequest##bits\ - {\ - REQUEST_HEADER_FIELDS\ - REQUEST_IDENTITY_FIELDS\ - REQUEST_GENERAL_FIELDS(bits)\ - REQUEST_TRAILER_FIELDS\ - };\ - -# define EXCEPTION_REQUEST_STATE(bits)\ - struct ExceptionRequestState##bits\ - {\ - REQUEST_HEADER_FIELDS\ - REQUEST_GENERAL_FIELDS(bits)\ - REQUEST_STATE_FIELDS\ - REQUEST_TRAILER_FIELDS\ - };\ - -# define EXCEPTION_REQUEST_STATE_IDENTITY(bits)\ - struct ExceptionRequestStateIdentity##bits\ - {\ - REQUEST_HEADER_FIELDS\ - REQUEST_IDENTITY_FIELDS\ - REQUEST_GENERAL_FIELDS(bits)\ - REQUEST_STATE_FIELDS\ - REQUEST_TRAILER_FIELDS\ - };\ - -/* This is needed because not all fields are naturally aligned on 64-bit. */ -# ifdef __MigPackStructs -# pragma pack(4) -# endif - -EXCEPTION_REQUEST(32) -EXCEPTION_REQUEST(64) -EXCEPTION_REQUEST_STATE(32) -EXCEPTION_REQUEST_STATE(64) -EXCEPTION_REQUEST_STATE_IDENTITY(32) -EXCEPTION_REQUEST_STATE_IDENTITY(64) - -/* We use this as a common base when forwarding to the previous handler. */ -union ExceptionRequestUnion { - mach_msg_header_t header; - ExceptionRequest32 r32; - ExceptionRequest64 r64; - ExceptionRequestState32 rs32; - ExceptionRequestState64 rs64; - ExceptionRequestStateIdentity32 rsi32; - ExceptionRequestStateIdentity64 rsi64; -}; - -/* This isn't really a full Mach message, but it's all we need to send. */ -struct ExceptionReply -{ - mach_msg_header_t header; - NDR_record_t NDR; - kern_return_t RetCode; -}; - -# ifdef __MigPackStructs -# pragma pack() -# endif - -# undef EXCEPTION_REQUEST_STATE_IDENTITY -# undef EXCEPTION_REQUEST_STATE -# undef EXCEPTION_REQUEST -# undef REQUEST_STATE_FIELDS -# undef REQUEST_GENERAL_FIELDS -# undef REQUEST_IDENTITY_FIELDS -# undef REQUEST_HEADER_FIELDS - -/* - * The exception handler we're forwarding to may not have the same behavior - * or thread state flavor as what we're using. These macros help populate - * the fields of the message we're about to send to the previous handler. - */ -# define COPY_REQUEST_COMMON(bits, id)\ - dst.header = src.header;\ - dst.header.msgh_id = id;\ - dst.header.msgh_size = static_cast(sizeof(dst) - sizeof(dst.trailer));\ - dst.NDR = src.NDR;\ - dst.exception = src.exception;\ - dst.code_count = src.code_count;\ - dst.code[0] = int##bits##_t(src.code[0]);\ - dst.code[1] = int##bits##_t(src.code[1]); - -# define COPY_REQUEST_IDENTITY\ - dst.msgh_body = src.msgh_body;\ - dst.thread = src.thread;\ - dst.task = src.task; - -# define COPY_REQUEST_STATE(flavor, stateCount, state)\ - mach_msg_size_t stateSize = stateCount * sizeof(natural_t);\ - dst.header.msgh_size = static_cast(sizeof(dst) - sizeof(dst.trailer) -\ - sizeof(dst.old_state) + stateSize);\ - dst.flavor = flavor;\ - dst.old_state_count = stateCount;\ - memcpy(dst.old_state, state, stateSize); - -# define COPY_EXCEPTION_REQUEST(bits)\ - static void\ - CopyExceptionRequest##bits(ExceptionRequest64& src,\ - ExceptionRequest##bits& dst)\ - {\ - COPY_REQUEST_COMMON(bits, sIDRequest##bits)\ - COPY_REQUEST_IDENTITY\ - } - -# define COPY_EXCEPTION_REQUEST_STATE(bits)\ - static void\ - CopyExceptionRequestState##bits(ExceptionRequest64& src,\ - ExceptionRequestState##bits& dst,\ - thread_state_flavor_t flavor,\ - mach_msg_type_number_t stateCount,\ - thread_state_t state)\ - {\ - COPY_REQUEST_COMMON(bits, sIDRequestState##bits)\ - COPY_REQUEST_STATE(flavor, stateCount, state)\ - } - -# define COPY_EXCEPTION_REQUEST_STATE_IDENTITY(bits)\ - static void\ - CopyExceptionRequestStateIdentity##bits(ExceptionRequest64& src,\ - ExceptionRequestStateIdentity##bits& dst,\ - thread_state_flavor_t flavor,\ - mach_msg_type_number_t stateCount,\ - thread_state_t state)\ - {\ - COPY_REQUEST_COMMON(bits, sIDRequestStateIdentity##bits)\ - COPY_REQUEST_IDENTITY\ - COPY_REQUEST_STATE(flavor, stateCount, state)\ - } - -COPY_EXCEPTION_REQUEST(32) -COPY_EXCEPTION_REQUEST_STATE(32) -COPY_EXCEPTION_REQUEST_STATE_IDENTITY(32) -COPY_EXCEPTION_REQUEST(64) -COPY_EXCEPTION_REQUEST_STATE(64) -COPY_EXCEPTION_REQUEST_STATE_IDENTITY(64) - -# undef COPY_EXCEPTION_REQUEST_STATE_IDENTITY -# undef COPY_EXCEPTION_REQUEST_STATE -# undef COPY_EXCEPTION_REQUEST -# undef COPY_REQUEST_STATE -# undef COPY_REQUEST_IDENTITY -# undef COPY_REQUEST_COMMON - -/* -------------------------------------------------------------------------- */ -/* End Mach definitions and helper functions */ -/* -------------------------------------------------------------------------- */ - -/* Every Mach exception handler is parameterized by these four properties. */ -struct MachExceptionParameters -{ - exception_mask_t mask; - mach_port_t port; - exception_behavior_t behavior; - thread_state_flavor_t flavor; -}; - -struct ExceptionHandlerState -{ - MachExceptionParameters current; - MachExceptionParameters previous; - - /* Each Mach exception handler runs in its own thread. */ - Thread handlerThread; -}; - -/* This choice of ID is arbitrary, but must not match our exception ID. */ -static const mach_msg_id_t sIDQuit = 42; - -static ExceptionHandlerState sMachExceptionState; - -/* - * The meat of our exception handler. This thread waits for an exception - * message, annotates the exception if needed, then forwards it to the - * previously installed handler (which will likely terminate the process). - */ -static void -MachExceptionHandler() -{ - kern_return_t ret; - MachExceptionParameters& current = sMachExceptionState.current; - MachExceptionParameters& previous = sMachExceptionState.previous; - - // We use the simplest kind of 64-bit exception message here. - ExceptionRequest64 request = {}; - request.header.msgh_local_port = current.port; - request.header.msgh_size = static_cast(sizeof(request)); - ret = mach_msg(&request.header, MACH_RCV_MSG, 0, request.header.msgh_size, - current.port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - - // Restore the previous handler. We're going to forward to it - // anyway, and if we crash while doing so we don't want to hang. - task_set_exception_ports(mach_task_self(), previous.mask, previous.port, - previous.behavior, previous.flavor); - - // If we failed even receiving the message, just give up. - if (ret != MACH_MSG_SUCCESS) - MOZ_CRASH("MachExceptionHandler: mach_msg failed to receive a message!"); - - // Terminate the thread if we're shutting down. - if (request.header.msgh_id == sIDQuit) - return; - - // The only other valid message ID is the one associated with the - // EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES behavior we chose. - if (request.header.msgh_id != sIDRequest64) - MOZ_CRASH("MachExceptionHandler: Unexpected Message ID!"); - - // Make sure we can understand the exception we received. - if (request.exception != EXC_BAD_ACCESS || request.code_count != 2) - MOZ_CRASH("MachExceptionHandler: Unexpected exception type!"); - - // Get the address that the offending code tried to access. - uintptr_t address = uintptr_t(request.code[1]); - - // If the faulting address is inside one of our protected regions, we - // want to annotate the crash to make it stand out from the crowd. - if (sProtectedRegions.isProtected(address)) { - ReportCrashIfDebug("Hit MOZ_CRASH(Tried to access a protected region!)\n"); - MOZ_CRASH_ANNOTATE("Tried to access a protected region!"); - } - - // Forward to the previous handler which may be a debugger, the unix - // signal handler, the crash reporter or something else entirely. - if (previous.port != MACH_PORT_NULL) { - mach_msg_type_number_t stateCount; - thread_state_data_t state; - if ((uint32_t(previous.behavior) & ~MACH_EXCEPTION_CODES) != EXCEPTION_DEFAULT) { - // If the previous handler requested thread state, get it here. - stateCount = THREAD_STATE_MAX; - ret = thread_get_state(request.thread.name, previous.flavor, state, &stateCount); - if (ret != KERN_SUCCESS) - MOZ_CRASH("MachExceptionHandler: Could not get the thread state to forward!"); - } - - // Depending on the behavior of the previous handler, the forwarded - // exception message will have a different set of fields. - // Of particular note is that exception handlers that lack - // MACH_EXCEPTION_CODES will get 32-bit fields even on 64-bit - // systems. It appears that OSX simply truncates these fields. - ExceptionRequestUnion forward; - switch (uint32_t(previous.behavior)) { - case EXCEPTION_DEFAULT: - CopyExceptionRequest32(request, forward.r32); - break; - case EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES: - CopyExceptionRequest64(request, forward.r64); - break; - case EXCEPTION_STATE: - CopyExceptionRequestState32(request, forward.rs32, - previous.flavor, stateCount, state); - break; - case EXCEPTION_STATE | MACH_EXCEPTION_CODES: - CopyExceptionRequestState64(request, forward.rs64, - previous.flavor, stateCount, state); - break; - case EXCEPTION_STATE_IDENTITY: - CopyExceptionRequestStateIdentity32(request, forward.rsi32, - previous.flavor, stateCount, state); - break; - case EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES: - CopyExceptionRequestStateIdentity64(request, forward.rsi64, - previous.flavor, stateCount, state); - break; - default: - MOZ_CRASH("MachExceptionHandler: Unknown previous handler behavior!"); - } - - // Forward the generated message to the old port. The local and remote - // port fields *and their rights* are swapped on arrival, so we need to - // swap them back first. - forward.header.msgh_bits = (request.header.msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) | - MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(request.header.msgh_bits), - MACH_MSGH_BITS_REMOTE(request.header.msgh_bits)); - forward.header.msgh_local_port = forward.header.msgh_remote_port; - forward.header.msgh_remote_port = previous.port; - ret = mach_msg(&forward.header, MACH_SEND_MSG, forward.header.msgh_size, 0, - MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if (ret != MACH_MSG_SUCCESS) - MOZ_CRASH("MachExceptionHandler: Failed to forward to the previous handler!"); - } else { - // There was no previous task-level exception handler, so defer to the - // host level one instead. We set the return code to KERN_FAILURE to - // indicate that we did not handle the exception. - // The reply message ID is always the request ID + 100. - ExceptionReply reply = {}; - reply.header.msgh_bits = - MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.header.msgh_bits), 0); - reply.header.msgh_size = static_cast(sizeof(reply)); - reply.header.msgh_remote_port = request.header.msgh_remote_port; - reply.header.msgh_local_port = MACH_PORT_NULL; - reply.header.msgh_id = request.header.msgh_id + 100; - reply.NDR = request.NDR; - reply.RetCode = KERN_FAILURE; - ret = mach_msg(&reply.header, MACH_SEND_MSG, reply.header.msgh_size, 0, - MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if (ret != MACH_MSG_SUCCESS) - MOZ_CRASH("MachExceptionHandler: Failed to forward to the host level!"); - } -} - -static void -TerminateMachExceptionHandlerThread() -{ - // Send a simple quit message to the exception handler thread. - mach_msg_header_t msg; - msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); - msg.msgh_size = static_cast(sizeof(msg)); - msg.msgh_remote_port = sMachExceptionState.current.port; - msg.msgh_local_port = MACH_PORT_NULL; - msg.msgh_reserved = 0; - msg.msgh_id = sIDQuit; - kern_return_t ret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - - if (ret == MACH_MSG_SUCCESS) - sMachExceptionState.handlerThread.join(); - else - MOZ_CRASH("MachExceptionHandler: Handler thread failed to terminate!"); -} - -bool -MemoryProtectionExceptionHandler::install() -{ - MOZ_ASSERT(!sExceptionHandlerInstalled); - - // If the exception handler is disabled, report success anyway. - if (MemoryProtectionExceptionHandler::isDisabled()) - return true; - - kern_return_t ret; - mach_port_t task = mach_task_self(); - - // Allocate a new exception port with receive rights. - sMachExceptionState.current = {}; - MachExceptionParameters& current = sMachExceptionState.current; - ret = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, ¤t.port); - if (ret != KERN_SUCCESS) - return false; - - // Give the new port send rights as well. - ret = mach_port_insert_right(task, current.port, current.port, MACH_MSG_TYPE_MAKE_SEND); - if (ret != KERN_SUCCESS) { - mach_port_deallocate(task, current.port); - current = {}; - return false; - } - - // Start the thread that will receive the messages from our exception port. - if (!sMachExceptionState.handlerThread.init(MachExceptionHandler)) { - mach_port_deallocate(task, current.port); - current = {}; - return false; - } - - // Set the other properties of our new exception handler. - current.mask = EXC_MASK_BAD_ACCESS; - current.behavior = exception_behavior_t(EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES); - current.flavor = THREAD_STATE_NONE; - - // Tell the task to use our exception handler, and save the previous one. - sMachExceptionState.previous = {}; - MachExceptionParameters& previous = sMachExceptionState.previous; - mach_msg_type_number_t previousCount = 1; - ret = task_swap_exception_ports(task, current.mask, current.port, current.behavior, - current.flavor, &previous.mask, &previousCount, - &previous.port, &previous.behavior, &previous.flavor); - if (ret != KERN_SUCCESS) { - TerminateMachExceptionHandlerThread(); - mach_port_deallocate(task, current.port); - previous = {}; - current = {}; - return false; - } - - // We should have info on the previous exception handler, even if it's null. - MOZ_ASSERT(previousCount == 1); - - sExceptionHandlerInstalled = true; - return sExceptionHandlerInstalled; -} - -void -MemoryProtectionExceptionHandler::uninstall() -{ - if (sExceptionHandlerInstalled) { - mach_port_t task = mach_task_self(); - - // Restore the previous exception handler. - MachExceptionParameters& previous = sMachExceptionState.previous; - task_set_exception_ports(task, previous.mask, previous.port, - previous.behavior, previous.flavor); - - TerminateMachExceptionHandlerThread(); - - // Release the Mach IPC port we used. - mach_port_deallocate(task, sMachExceptionState.current.port); - - sMachExceptionState.current = {}; - sMachExceptionState.previous = {}; - - sExceptionHandlerInstalled = false; - } -} - -#else - -#error "This platform is not supported!" - -#endif - -} /* namespace js */ diff --git a/js/src/ds/MemoryProtectionExceptionHandler.h b/js/src/ds/MemoryProtectionExceptionHandler.h deleted file mode 100644 index 0fcb056126f3..000000000000 --- a/js/src/ds/MemoryProtectionExceptionHandler.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ds_MemoryProtectionExceptionHandler_h -#define ds_MemoryProtectionExceptionHandler_h - -namespace js { - -/* - * This structure allows you to annotate crashes resulting from unauthorized - * access to protected memory in regions of interest, to make them stand out - * from other heap corruption crashes. - */ - -struct MemoryProtectionExceptionHandler -{ - /* Installs the exception handler; called early during initialization. */ - static bool install(); - - /* If the exception handler is disabled, it won't be installed. */ - static bool isDisabled(); - - /* - * Marks a region of memory as important; if something tries to access this - * region in an unauthorized way (e.g. writing to read-only memory), - * the resulting crash will be annotated to stand out from other random - * heap corruption. - */ - static void addRegion(void* addr, size_t size); - - /* Removes a previously added region. */ - static void removeRegion(void* addr); - - /* Uninstalls the exception handler; called late during shutdown. */ - static void uninstall(); -}; - -} /* namespace js */ - -#endif /* ds_MemoryProtectionExceptionHandler_h */ diff --git a/js/src/ds/PageProtectingVector.h b/js/src/ds/PageProtectingVector.h index 99a703db688c..f6e3e8ca30c3 100644 --- a/js/src/ds/PageProtectingVector.h +++ b/js/src/ds/PageProtectingVector.h @@ -9,7 +9,6 @@ #include "mozilla/Vector.h" -#include "ds/MemoryProtectionExceptionHandler.h" #include "gc/Memory.h" namespace js { @@ -59,13 +58,6 @@ class PageProtectingVector final */ intptr_t unprotectedBytes; - /* - * The size in bytes that a buffer needs to be before its pages will be - * protected. This is intended to reduce churn for small vectors while - * still offering protection when they grow large enough. - */ - size_t protectionLowerBound; - bool protectionEnabled; bool regionUnprotected; @@ -73,8 +65,6 @@ class PageProtectingVector final unprotectedBytes += offsetToPage; offsetToPage = (pageSize - (uintptr_t(vector.begin()) & pageMask)) & pageMask; unprotectedBytes -= offsetToPage; - protectionEnabled = vector.capacity() >= protectionLowerBound && - vector.capacity() >= pageSize + offsetToPage; } void protect() { @@ -101,17 +91,9 @@ class PageProtectingVector final void protectNewBuffer() { updateOffsetToPage(); - if (protectionEnabled) - MemoryProtectionExceptionHandler::addRegion(vector.begin(), vector.capacity()); protect(); } - void unprotectOldBuffer() { - if (protectionEnabled) - MemoryProtectionExceptionHandler::removeRegion(vector.begin()); - unprotect(); - } - bool anyProtected(size_t first, size_t last) { return last >= offsetToPage && first < offsetToPage + protectedBytes; } @@ -143,7 +125,7 @@ class PageProtectingVector final void emplace(PageProtectingVector* holder) { vector = holder; - vector->unprotectOldBuffer(); + vector->unprotect(); } explicit AutoUnprotect(PageProtectingVector* holder) { @@ -164,22 +146,23 @@ class PageProtectingVector final offsetToPage(0), protectedBytes(0), unprotectedBytes(0), - protectionLowerBound(0), protectionEnabled(false), - regionUnprotected(false) { protectNewBuffer(); } + regionUnprotected(false) { updateOffsetToPage(); } - ~PageProtectingVector() { unprotectOldBuffer(); } + ~PageProtectingVector() { unprotect(); } - /* - * Sets the lower bound on the size, in bytes, that this vector's underlying - * capacity has to be before its used pages will be protected. - */ - void setLowerBoundForProtection(size_t bytes) { - if (protectionLowerBound != bytes) { - unprotectOldBuffer(); - protectionLowerBound = bytes; - protectNewBuffer(); - } + /* Enable protection for the entire buffer. */ + void enableProtection() { + MOZ_ASSERT(!protectionEnabled); + protectionEnabled = true; + protectNewBuffer(); + } + + /* Disable protection for the entire buffer. */ + void disableProtection() { + MOZ_ASSERT(protectionEnabled); + unprotect(); + protectionEnabled = false; } /* diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h index 6243c1822183..63f4c009dbc7 100644 --- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h +++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h @@ -87,8 +87,6 @@ namespace jit { AssemblerBuffer() : m_oom(false) { - // Provide memory protection once the buffer starts to get big. - m_buffer.setLowerBoundForProtection(32 * 1024); } void ensureSpace(size_t space) @@ -141,6 +139,9 @@ namespace jit { return m_buffer.begin(); } + void enableBufferProtection() { m_buffer.enableProtection(); } + void disableBufferProtection() { m_buffer.disableProtection(); } + void unprotectDataRegion(size_t firstByteOffset, size_t lastByteOffset) { m_buffer.unprotectRegion(firstByteOffset, lastByteOffset); } diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 844fd5c0ed9c..3c4fcd5caf90 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -3851,6 +3851,9 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off return m_formatter.append(other.m_formatter.buffer(), other.size()); } + void enableBufferProtection() { m_formatter.enableBufferProtection(); } + void disableBufferProtection() { m_formatter.disableBufferProtection(); } + void unprotectDataRegion(size_t firstByteOffset, size_t lastByteOffset) { m_formatter.unprotectDataRegion(firstByteOffset, lastByteOffset); } @@ -5129,6 +5132,9 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off return m_buffer.append(values, size); } + void enableBufferProtection() { m_buffer.enableBufferProtection(); } + void disableBufferProtection() { m_buffer.disableBufferProtection(); } + void unprotectDataRegion(size_t firstByteOffset, size_t lastByteOffset) { m_buffer.unprotectDataRegion(firstByteOffset, lastByteOffset); } diff --git a/js/src/moz.build b/js/src/moz.build index 2fd646473ad1..9b36547dc261 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -194,7 +194,6 @@ UNIFIED_SOURCES += [ 'builtin/WeakSetObject.cpp', 'devtools/sharkctl.cpp', 'ds/LifoAlloc.cpp', - 'ds/MemoryProtectionExceptionHandler.cpp', 'frontend/BytecodeCompiler.cpp', 'frontend/BytecodeEmitter.cpp', 'frontend/FoldConstants.cpp', diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp index 807d90f3e957..2bd61aa8dbc9 100644 --- a/js/src/vm/Initialization.cpp +++ b/js/src/vm/Initialization.cpp @@ -15,7 +15,6 @@ #include "jstypes.h" #include "builtin/AtomicsObject.h" -#include "ds/MemoryProtectionExceptionHandler.h" #include "gc/Statistics.h" #include "jit/ExecutableAllocator.h" #include "jit/Ion.h" @@ -101,8 +100,6 @@ JS::detail::InitWithFailureDiagnostic(bool isDebugBuild) js::jit::ExecutableAllocator::initStatic(); - MOZ_ALWAYS_TRUE(js::MemoryProtectionExceptionHandler::install()); - RETURN_IF_FAIL(js::jit::InitializeIon()); js::DateTimeInfo::init(); @@ -148,8 +145,6 @@ JS_ShutDown(void) js::DestroyTraceLoggerGraphState(); #endif - js::MemoryProtectionExceptionHandler::uninstall(); - // The only difficult-to-address reason for the restriction that you can't // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ // NowInit initialization code, which uses PR_CallOnce to initialize the