diff options
author | Moonchild <moonchild@palemoon.org> | 2021-05-02 07:10:18 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2021-05-02 07:10:18 +0000 |
commit | 3d671e4275c73a1484c72713304c6e04ec4ffc7c (patch) | |
tree | 98c4e605f3ce273f65fdf208275c433ee4566d4c /js/src/ds/MemoryProtectionExceptionHandler.cpp | |
parent | 56da7e27477d0d4669980d2ce17f3b877ea0c36c (diff) | |
download | uxp-3d671e4275c73a1484c72713304c6e04ec4ffc7c.tar.gz |
Issue #1751 -- Remove XP_DARWIN
Diffstat (limited to 'js/src/ds/MemoryProtectionExceptionHandler.cpp')
-rw-r--r-- | js/src/ds/MemoryProtectionExceptionHandler.cpp | 466 |
1 files changed, 2 insertions, 464 deletions
diff --git a/js/src/ds/MemoryProtectionExceptionHandler.cpp b/js/src/ds/MemoryProtectionExceptionHandler.cpp index 06fabb5301..77df1f0d92 100644 --- a/js/src/ds/MemoryProtectionExceptionHandler.cpp +++ b/js/src/ds/MemoryProtectionExceptionHandler.cpp @@ -10,13 +10,10 @@ #if defined(XP_WIN) # include "jswin.h" -#elif defined(XP_UNIX) && !defined(XP_DARWIN) +#elif defined(XP_UNIX) # include <signal.h> # include <sys/types.h> # include <unistd.h> -#elif defined(XP_DARWIN) -# include <mach/mach.h> -# include <unistd.h> #endif #ifdef ANDROID @@ -210,7 +207,7 @@ MemoryProtectionExceptionHandler::uninstall() } } -#elif defined(XP_UNIX) && !defined(XP_DARWIN) +#elif defined(XP_UNIX) static struct sigaction sPrevSEGVHandler = {}; @@ -291,465 +288,6 @@ MemoryProtectionExceptionHandler::uninstall() } } -#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<mach_msg_size_t>(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<mach_msg_size_t>(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<mach_msg_size_t>(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("MOZ_CRASH(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<mach_msg_size_t>(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<mach_msg_size_t>(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!" |