Backed out 2 changesets (bug 1305360) for sm(nu) failures a=backout

Backed out changeset a70dda2a48b4 (bug 1305360)
Backed out changeset eab215763463 (bug 1305360)
This commit is contained in:
Wes Kocher 2016-09-29 13:52:09 -07:00
parent 220518855d
commit 534d31c7d0
7 changed files with 24 additions and 836 deletions

View File

@ -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 <signal.h>
# include <sys/types.h>
# include <unistd.h>
#elif defined(XP_DARWIN)
# include <mach/mach.h>
#endif
#ifdef ANDROID
# include <android/log.h>
#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<Region, Region> 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<Mutex> 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<Mutex> guard(lock);
tree.remove(Region(addr, 1));
}
bool isProtected(uintptr_t addr) {
if (!addr)
return false;
LockGuard<Mutex> 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<bool> 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<bool> 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<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("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, &current.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 */

View File

@ -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 */

View File

@ -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;
}
/*

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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',

View File

@ -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