CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/ExceptionHandlerSetup.cpp
Views: 1401
// Copyright 2008 Dolphin Emulator Project1// Licensed under GPLv2+2// Refer to the license.txt file included.34// The corresponding file is called MemTools in the Dolphin project.56#include "ppsspp_config.h"78#include <cstdio>9#include <cstdlib>10#include <cstring>11#include <vector>12#include <thread>1314#include "Common/CommonFuncs.h"15#include "Common/CommonTypes.h"16#include "Common/Log.h"17#include "Common/Thread/ThreadUtil.h"18#include "Common/MachineContext.h"19#include "Common/ExceptionHandlerSetup.h"2021static BadAccessHandler g_badAccessHandler;22static void *altStack = nullptr;2324#ifdef MACHINE_CONTEXT_SUPPORTED2526// We cannot handle exceptions in UWP builds. Bleh.27#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)2829static PVOID g_vectoredExceptionHandle;3031static LONG NTAPI GlobalExceptionHandler(PEXCEPTION_POINTERS pPtrs) {32switch (pPtrs->ExceptionRecord->ExceptionCode) {33case EXCEPTION_ACCESS_VIOLATION:34{35int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0];36if (accessType == 8) { // Rule out DEP37return (DWORD)EXCEPTION_CONTINUE_SEARCH;38}3940// virtual address of the inaccessible data41uintptr_t badAddress = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1];42CONTEXT* ctx = pPtrs->ContextRecord;4344if (g_badAccessHandler(badAddress, ctx)) {45return (DWORD)EXCEPTION_CONTINUE_EXECUTION;46} else {47// Let's not prevent debugging.48return (DWORD)EXCEPTION_CONTINUE_SEARCH;49}50}5152case EXCEPTION_STACK_OVERFLOW:53// Dolphin has some handling of this for the RET optimization emulation.54return EXCEPTION_CONTINUE_SEARCH;5556case EXCEPTION_ILLEGAL_INSTRUCTION:57// No SSE support? Or simply bad codegen?58return EXCEPTION_CONTINUE_SEARCH;5960case EXCEPTION_PRIV_INSTRUCTION:61// okay, dynarec codegen is obviously broken.62return EXCEPTION_CONTINUE_SEARCH;6364case EXCEPTION_IN_PAGE_ERROR:65// okay, something went seriously wrong, out of memory?66return EXCEPTION_CONTINUE_SEARCH;6768case EXCEPTION_BREAKPOINT:69// might want to do something fun with this one day?70return EXCEPTION_CONTINUE_SEARCH;7172default:73return EXCEPTION_CONTINUE_SEARCH;74}75}7677void InstallExceptionHandler(BadAccessHandler badAccessHandler) {78if (g_vectoredExceptionHandle) {79g_badAccessHandler = badAccessHandler;80return;81}8283INFO_LOG(Log::System, "Installing exception handler");84g_badAccessHandler = badAccessHandler;85g_vectoredExceptionHandle = AddVectoredExceptionHandler(TRUE, GlobalExceptionHandler);86}8788void UninstallExceptionHandler() {89if (g_vectoredExceptionHandle) {90RemoveVectoredExceptionHandler(g_vectoredExceptionHandle);91INFO_LOG(Log::System, "Removed exception handler");92g_vectoredExceptionHandle = nullptr;93}94g_badAccessHandler = nullptr;95}9697#elif defined(__APPLE__)9899static void CheckKR(const char* name, kern_return_t kr) {100_assert_msg_(kr == 0, "%s failed: kr=%x", name, kr);101}102103static void ExceptionThread(mach_port_t port) {104SetCurrentThreadName("Mach exception thread");105#pragma pack(4)106struct {107mach_msg_header_t Head;108NDR_record_t NDR;109exception_type_t exception;110mach_msg_type_number_t codeCnt;111int64_t code[2];112int flavor;113mach_msg_type_number_t old_stateCnt;114natural_t old_state[x86_THREAD_STATE64_COUNT];115mach_msg_trailer_t trailer;116} msg_in;117118struct {119mach_msg_header_t Head;120NDR_record_t NDR;121kern_return_t RetCode;122int flavor;123mach_msg_type_number_t new_stateCnt;124natural_t new_state[x86_THREAD_STATE64_COUNT];125} msg_out;126#pragma pack()127memset(&msg_in, 0xee, sizeof(msg_in));128memset(&msg_out, 0xee, sizeof(msg_out));129mach_msg_header_t* send_msg = &msg_out.Head;130mach_msg_size_t send_size = 0;131mach_msg_option_t option = MACH_RCV_MSG;132while (true) {133// If this isn't the first run, send the reply message. Then, receive134// a message: either a mach_exception_raise_state RPC due to135// thread_set_exception_ports, or MACH_NOTIFY_NO_SENDERS due to136// mach_port_request_notification.137CheckKR("mach_msg_overwrite",138mach_msg_overwrite(send_msg, option, send_size, sizeof(msg_in), port,139MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0));140141if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS) {142// the other thread exited143mach_port_destroy(mach_task_self(), port);144return;145}146147_assert_msg_(msg_in.Head.msgh_id == 2406, "unknown message received");148_assert_msg_(msg_in.flavor == x86_THREAD_STATE64, "unknown flavor %d (expected %d)", msg_in.flavor, x86_THREAD_STATE64);149150x86_thread_state64_t* state = (x86_thread_state64_t*)msg_in.old_state;151152bool ok = g_badAccessHandler((uintptr_t)msg_in.code[1], state);153154// Set up the reply.155msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);156msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;157msg_out.Head.msgh_local_port = MACH_PORT_NULL;158msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;159msg_out.NDR = msg_in.NDR;160if (ok) {161msg_out.RetCode = KERN_SUCCESS;162msg_out.flavor = x86_THREAD_STATE64;163msg_out.new_stateCnt = x86_THREAD_STATE64_COUNT;164memcpy(msg_out.new_state, msg_in.old_state, x86_THREAD_STATE64_COUNT * sizeof(natural_t));165} else {166// Pass the exception to the next handler (debugger or crash).167msg_out.RetCode = KERN_FAILURE;168msg_out.flavor = 0;169msg_out.new_stateCnt = 0;170}171msg_out.Head.msgh_size =172offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);173174send_msg = &msg_out.Head;175send_size = msg_out.Head.msgh_size;176option |= MACH_SEND_MSG;177}178}179180void InstallExceptionHandler(BadAccessHandler badAccessHandler) {181if (g_badAccessHandler) {182// The rest of the setup we don't need to do again.183g_badAccessHandler = badAccessHandler;184return;185}186g_badAccessHandler = badAccessHandler;187188INFO_LOG(Log::System, "Installing exception handler");189mach_port_t port;190CheckKR("mach_port_allocate",191mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port));192std::thread exc_thread(ExceptionThread, port);193exc_thread.detach();194// Obtain a send right for thread_set_exception_ports to copy...195CheckKR("mach_port_insert_right",196mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND));197// Mach tries the following exception ports in order: thread, task, host.198// Debuggers set the task port, so we grab the thread port.199CheckKR("thread_set_exception_ports",200thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port,201EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64));202// ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.203CheckKR("mach_port_mod_refs",204mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));205mach_port_t previous;206CheckKR("mach_port_request_notification",207mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port,208MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));209}210211void UninstallExceptionHandler() {212}213214#else215216#include <signal.h>217218static struct sigaction old_sa_segv;219static struct sigaction old_sa_bus;220221static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {222if (sig != SIGSEGV && sig != SIGBUS) {223// We are not interested in other signals - handle it as usual.224return;225}226ucontext_t* context = (ucontext_t*)raw_context;227int sicode = info->si_code;228if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR) {229// Huh? Return.230return;231}232uintptr_t bad_address = (uintptr_t)info->si_addr;233234// Get all the information we can out of the context.235#ifdef __OpenBSD__236ucontext_t* ctx = context;237#else238mcontext_t* ctx = &context->uc_mcontext;239#endif240// assume it's not a write241if (!g_badAccessHandler(bad_address,242#ifdef __APPLE__243*ctx244#else245ctx246#endif247)) {248// retry and crash249// According to the sigaction man page, if sa_flags "SA_SIGINFO" is set to the sigaction250// function pointer, otherwise sa_handler contains one of:251// SIG_DEF: The 'default' action is performed252// SIG_IGN: The signal is ignored253// Any other value is a function pointer to a signal handler254255struct sigaction* old_sa;256if (sig == SIGSEGV) {257old_sa = &old_sa_segv;258} else {259old_sa = &old_sa_bus;260}261262if (old_sa->sa_flags & SA_SIGINFO) {263old_sa->sa_sigaction(sig, info, raw_context);264return;265}266if (old_sa->sa_handler == SIG_DFL) {267signal(sig, SIG_DFL);268return;269}270if (old_sa->sa_handler == SIG_IGN) {271// Ignore signal272return;273}274old_sa->sa_handler(sig);275}276}277278void InstallExceptionHandler(BadAccessHandler badAccessHandler) {279if (!badAccessHandler) {280return;281}282if (g_badAccessHandler) {283g_badAccessHandler = badAccessHandler;284return;285}286287size_t altStackSize = SIGSTKSZ;288289// Add some extra room.290altStackSize += 65536;291292INFO_LOG(Log::System, "Installed exception handler. stack size: %d", (int)altStackSize);293g_badAccessHandler = badAccessHandler;294295stack_t signal_stack{};296altStack = malloc(altStackSize);297#ifdef __FreeBSD__298signal_stack.ss_sp = (char*)altStack;299#else300signal_stack.ss_sp = altStack;301#endif302signal_stack.ss_size = altStackSize;303signal_stack.ss_flags = 0;304if (sigaltstack(&signal_stack, nullptr)) {305_assert_msg_(false, "sigaltstack failed");306}307struct sigaction sa{};308sa.sa_handler = nullptr;309sa.sa_sigaction = &sigsegv_handler;310sa.sa_flags = SA_SIGINFO | SA_ONSTACK;311sigemptyset(&sa.sa_mask);312sigaction(SIGSEGV, &sa, &old_sa_segv);313#ifdef __APPLE__314sigaction(SIGBUS, &sa, &old_sa_bus);315#endif316}317318void UninstallExceptionHandler() {319if (!g_badAccessHandler) {320return;321}322stack_t signal_stack{};323signal_stack.ss_flags = SS_DISABLE;324if (0 != sigaltstack(&signal_stack, nullptr)) {325ERROR_LOG(Log::System, "Could not remove signal altstack");326}327if (altStack) {328free(altStack);329altStack = nullptr;330}331sigaction(SIGSEGV, &old_sa_segv, nullptr);332#ifdef __APPLE__333sigaction(SIGBUS, &old_sa_bus, nullptr);334#endif335INFO_LOG(Log::System, "Uninstalled exception handler");336g_badAccessHandler = nullptr;337}338339#endif340341#else // !MACHINE_CONTEXT_SUPPORTED342343void InstallExceptionHandler(BadAccessHandler badAccessHandler) {344ERROR_LOG(Log::System, "Exception handler not implemented on this platform, can't install");345}346void UninstallExceptionHandler() { }347348#endif // MACHINE_CONTEXT_SUPPORTED349350351