#include "wasm/WasmSignalHandlers.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ThreadLocal.h"
#include "threading/Thread.h"
#include "vm/Realm.h"
#include "vm/Runtime.h"
#include "wasm/WasmInstance.h"
#if defined(XP_WIN)
# include <winternl.h>
# include "util/Windows.h"
#elif defined(XP_DARWIN)
# include <mach/exc.h>
# include <mach/mach.h>
#else
# include <signal.h>
#endif
using namespace js;
using namespace js::wasm;
using mozilla::DebugOnly;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
# include <sys/ucontext.h>
#endif
#if defined(__x86_64__)
# if defined(__DragonFly__)
# include <machine/npx.h>
# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
# include <machine/fpu.h>
# endif
#endif
#if defined(XP_WIN)
# define EIP_sig(p) ((p)->Eip)
# define EBP_sig(p) ((p)->Ebp)
# define ESP_sig(p) ((p)->Esp)
# define RIP_sig(p) ((p)->Rip)
# define RSP_sig(p) ((p)->Rsp)
# define RBP_sig(p) ((p)->Rbp)
# define R11_sig(p) ((p)->R11)
# define R13_sig(p) ((p)->R13)
# define R14_sig(p) ((p)->R14)
# define R15_sig(p) ((p)->R15)
# define EPC_sig(p) ((p)->Pc)
# define RFP_sig(p) ((p)->Fp)
# define R31_sig(p) ((p)->Sp)
# define RLR_sig(p) ((p)->Lr)
#elif defined(__OpenBSD__)
# define EIP_sig(p) ((p)->sc_eip)
# define EBP_sig(p) ((p)->sc_ebp)
# define ESP_sig(p) ((p)->sc_esp)
# define RIP_sig(p) ((p)->sc_rip)
# define RSP_sig(p) ((p)->sc_rsp)
# define RBP_sig(p) ((p)->sc_rbp)
# define R11_sig(p) ((p)->sc_r11)
# if defined(__arm__)
# define R13_sig(p) ((p)->sc_usr_sp)
# define R14_sig(p) ((p)->sc_usr_lr)
# define R15_sig(p) ((p)->sc_pc)
# else
# define R13_sig(p) ((p)->sc_r13)
# define R14_sig(p) ((p)->sc_r14)
# define R15_sig(p) ((p)->sc_r15)
# endif
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->sc_elr)
# define RFP_sig(p) ((p)->sc_x[29])
# define RLR_sig(p) ((p)->sc_lr)
# define R31_sig(p) ((p)->sc_sp)
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->sc_pc)
# define RFP_sig(p) ((p)->sc_regs[30])
# endif
# if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
defined(__PPC64LE__)
# define R01_sig(p) ((p)->sc_frame.fixreg[1])
# define R32_sig(p) ((p)->sc_frame.srr0)
# endif
#elif defined(__linux__) || defined(__sun)
# if defined(__linux__)
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
# define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.gregs[REG_ESP])
# else
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC])
# define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.gregs[REG_ESP])
# endif
# define RIP_sig(p) ((p)->uc_mcontext.gregs[REG_RIP])
# define RSP_sig(p) ((p)->uc_mcontext.gregs[REG_RSP])
# define RBP_sig(p) ((p)->uc_mcontext.gregs[REG_RBP])
# if defined(__linux__) && defined(__arm__)
# define R11_sig(p) ((p)->uc_mcontext.arm_fp)
# define R13_sig(p) ((p)->uc_mcontext.arm_sp)
# define R14_sig(p) ((p)->uc_mcontext.arm_lr)
# define R15_sig(p) ((p)->uc_mcontext.arm_pc)
# else
# define R11_sig(p) ((p)->uc_mcontext.gregs[REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.gregs[REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.gregs[REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.gregs[REG_R15])
# endif
# if defined(__linux__) && defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.pc)
# define RFP_sig(p) ((p)->uc_mcontext.regs[29])
# define RLR_sig(p) ((p)->uc_mcontext.regs[30])
# define R31_sig(p) ((p)->uc_mcontext.regs[31])
# endif
# if defined(__linux__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.pc)
# define RFP_sig(p) ((p)->uc_mcontext.gregs[30])
# define RSP_sig(p) ((p)->uc_mcontext.gregs[29])
# define R31_sig(p) ((p)->uc_mcontext.gregs[31])
# endif
# if defined(__linux__) && (defined(__sparc__) && defined(__arch64__))
# define PC_sig(p) ((p)->uc_mcontext.mc_gregs[MC_PC])
# define FP_sig(p) ((p)->uc_mcontext.mc_fp)
# define SP_sig(p) ((p)->uc_mcontext.mc_i7)
# endif
# if defined(__linux__) && (defined(__ppc64__) || defined(__PPC64__) || \
defined(__ppc64le__) || defined(__PPC64LE__))
# define R01_sig(p) ((p)->uc_mcontext.gp_regs[1])
# define R32_sig(p) ((p)->uc_mcontext.gp_regs[32])
# endif
#elif defined(__NetBSD__)
# define EIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EIP])
# define EBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.__gregs[_REG_ESP])
# define RIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RIP])
# define RSP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSP])
# define RBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBP])
# define R11_sig(p) ((p)->uc_mcontext.__gregs[_REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_PC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_X29])
# define RLR_sig(p) ((p)->uc_mcontext.__gregs[_REG_X30])
# define R31_sig(p) ((p)->uc_mcontext.__gregs[_REG_SP])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_EPC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_S8])
# endif
# if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
defined(__PPC64LE__)
# define R01_sig(p) ((p)->uc_mcontext.__gregs[_REG_R1])
# define R32_sig(p) ((p)->uc_mcontext.__gregs[_REG_PC])
# endif
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__)
# define EIP_sig(p) ((p)->uc_mcontext.mc_eip)
# define EBP_sig(p) ((p)->uc_mcontext.mc_ebp)
# define ESP_sig(p) ((p)->uc_mcontext.mc_esp)
# define RIP_sig(p) ((p)->uc_mcontext.mc_rip)
# define RSP_sig(p) ((p)->uc_mcontext.mc_rsp)
# define RBP_sig(p) ((p)->uc_mcontext.mc_rbp)
# if defined(__FreeBSD__) && defined(__arm__)
# define R11_sig(p) ((p)->uc_mcontext.__gregs[_REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
# else
# define R11_sig(p) ((p)->uc_mcontext.mc_r11)
# define R13_sig(p) ((p)->uc_mcontext.mc_r13)
# define R14_sig(p) ((p)->uc_mcontext.mc_r14)
# define R15_sig(p) ((p)->uc_mcontext.mc_r15)
# endif
# if defined(__FreeBSD__) && defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_elr)
# define RFP_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_x[29])
# define RLR_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_lr)
# define R31_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_sp)
# endif
# if defined(__FreeBSD__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
# define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30])
# endif
# if defined(__FreeBSD__) && (defined(__ppc64__) || defined(__PPC64__) || \
defined(__ppc64le__) || defined(__PPC64LE__))
# define R01_sig(p) ((p)->uc_mcontext.mc_gpr[1])
# define R32_sig(p) ((p)->uc_mcontext.mc_srr0)
# endif
#elif defined(XP_DARWIN)
# define EIP_sig(p) ((p)->thread.uts.ts32.__eip)
# define EBP_sig(p) ((p)->thread.uts.ts32.__ebp)
# define ESP_sig(p) ((p)->thread.uts.ts32.__esp)
# define RIP_sig(p) ((p)->thread.__rip)
# define RBP_sig(p) ((p)->thread.__rbp)
# define RSP_sig(p) ((p)->thread.__rsp)
# define R11_sig(p) ((p)->thread.__r[11])
# define R13_sig(p) ((p)->thread.__sp)
# define R14_sig(p) ((p)->thread.__lr)
# define R15_sig(p) ((p)->thread.__pc)
#else
# error "Don't know how to read/write to the thread state via the mcontext_t."
#endif
#if defined(__linux__) && defined(__arm__)
# define WASM_EMULATE_ARM_UNALIGNED_FP_ACCESS
#endif
#ifdef WASM_EMULATE_ARM_UNALIGNED_FP_ACCESS
# include <sys/user.h>
#endif
#if defined(ANDROID)
# if !defined(__BIONIC_HAVE_UCONTEXT_T)
# if defined(__arm__)
# if !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
# include <asm/sigcontext.h>
# endif
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
} ucontext_t;
# elif defined(__mips__)
typedef struct {
uint32_t regmask;
uint32_t status;
uint64_t pc;
uint64_t gregs[32];
uint64_t fpregs[32];
uint32_t acx;
uint32_t fpc_csr;
uint32_t fpc_eir;
uint32_t used_math;
uint32_t dsp;
uint64_t mdhi;
uint64_t mdlo;
uint32_t hi1;
uint32_t lo1;
uint32_t hi2;
uint32_t lo2;
uint32_t hi3;
uint32_t lo3;
} mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
} ucontext_t;
# elif defined(__i386__)
typedef struct {
uint32_t gregs[19];
void* fpregs;
uint32_t oldmask;
uint32_t cr2;
} mcontext_t;
typedef uint32_t kernel_sigset_t[2]; typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
} ucontext_t;
enum { REG_EIP = 14 };
# endif # endif #endif
#if defined(XP_DARWIN)
# if defined(__x86_64__)
struct macos_x64_context {
x86_thread_state64_t thread;
x86_float_state64_t float_;
};
# define CONTEXT macos_x64_context
# elif defined(__i386__)
struct macos_x86_context {
x86_thread_state_t thread;
x86_float_state_t float_;
};
# define CONTEXT macos_x86_context
# elif defined(__arm__)
struct macos_arm_context {
arm_thread_state_t thread;
arm_neon_state_t float_;
};
# define CONTEXT macos_arm_context
# else
# error Unsupported architecture
# endif
#elif !defined(XP_WIN)
# define CONTEXT ucontext_t
#endif
#if defined(_M_X64) || defined(__x86_64__)
# define PC_sig(p) RIP_sig(p)
# define FP_sig(p) RBP_sig(p)
# define SP_sig(p) RSP_sig(p)
#elif defined(_M_IX86) || defined(__i386__)
# define PC_sig(p) EIP_sig(p)
# define FP_sig(p) EBP_sig(p)
# define SP_sig(p) ESP_sig(p)
#elif defined(__arm__)
# define FP_sig(p) R11_sig(p)
# define SP_sig(p) R13_sig(p)
# define LR_sig(p) R14_sig(p)
# define PC_sig(p) R15_sig(p)
#elif defined(_M_ARM64) || defined(__aarch64__)
# define PC_sig(p) EPC_sig(p)
# define FP_sig(p) RFP_sig(p)
# define SP_sig(p) R31_sig(p)
# define LR_sig(p) RLR_sig(p)
#elif defined(__mips__)
# define PC_sig(p) EPC_sig(p)
# define FP_sig(p) RFP_sig(p)
# define SP_sig(p) RSP_sig(p)
# define LR_sig(p) R31_sig(p)
#elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
defined(__PPC64LE__)
# define PC_sig(p) R32_sig(p)
# define SP_sig(p) R01_sig(p)
# define FP_sig(p) R01_sig(p)
#endif
static void SetContextPC(CONTEXT* context, uint8_t* pc) {
#ifdef PC_sig
*reinterpret_cast<uint8_t**>(&PC_sig(context)) = pc;
#else
MOZ_CRASH();
#endif
}
static uint8_t* ContextToPC(CONTEXT* context) {
#ifdef PC_sig
return reinterpret_cast<uint8_t*>(PC_sig(context));
#else
MOZ_CRASH();
#endif
}
static uint8_t* ContextToFP(CONTEXT* context) {
#ifdef FP_sig
return reinterpret_cast<uint8_t*>(FP_sig(context));
#else
MOZ_CRASH();
#endif
}
static uint8_t* ContextToSP(CONTEXT* context) {
#ifdef SP_sig
return reinterpret_cast<uint8_t*>(SP_sig(context));
#else
MOZ_CRASH();
#endif
}
#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
static uint8_t* ContextToLR(CONTEXT* context) {
# ifdef LR_sig
return reinterpret_cast<uint8_t*>(LR_sig(context));
# else
MOZ_CRASH();
# endif
}
#endif
static JS::ProfilingFrameIterator::RegisterState ToRegisterState(
CONTEXT* context) {
JS::ProfilingFrameIterator::RegisterState state;
state.fp = ContextToFP(context);
state.pc = ContextToPC(context);
state.sp = ContextToSP(context);
#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
state.lr = ContextToLR(context);
#else
state.lr = (void*)UINTPTR_MAX;
#endif
return state;
}
static MOZ_THREAD_LOCAL(bool) sAlreadyHandlingTrap;
struct AutoHandlingTrap {
AutoHandlingTrap() {
MOZ_ASSERT(!sAlreadyHandlingTrap.get());
sAlreadyHandlingTrap.set(true);
}
~AutoHandlingTrap() {
MOZ_ASSERT(sAlreadyHandlingTrap.get());
sAlreadyHandlingTrap.set(false);
}
};
#ifdef WASM_EMULATE_ARM_UNALIGNED_FP_ACCESS
static uintptr_t ReadGPR(CONTEXT* context, uint32_t rn) {
switch (rn) {
case 0:
return context->uc_mcontext.arm_r0;
case 1:
return context->uc_mcontext.arm_r1;
case 2:
return context->uc_mcontext.arm_r2;
case 3:
return context->uc_mcontext.arm_r3;
case 4:
return context->uc_mcontext.arm_r4;
case 5:
return context->uc_mcontext.arm_r5;
case 6:
return context->uc_mcontext.arm_r6;
case 7:
return context->uc_mcontext.arm_r7;
case 8:
return context->uc_mcontext.arm_r8;
case 9:
return context->uc_mcontext.arm_r9;
case 10:
return context->uc_mcontext.arm_r10;
case 11:
return context->uc_mcontext.arm_fp;
case 12:
return context->uc_mcontext.arm_ip;
case 13:
return context->uc_mcontext.arm_sp;
case 14:
return context->uc_mcontext.arm_lr;
case 15:
return context->uc_mcontext.arm_pc;
default:
MOZ_CRASH();
}
}
struct vfp_sigframe {
unsigned long magic;
unsigned long size;
struct user_vfp ufp;
struct user_vfp_exc ufp_exc;
};
# define VFP_MAGIC 0x56465001
static vfp_sigframe* GetVFPFrame(CONTEXT* context) {
if (context->uc_regspace[0] != VFP_MAGIC) {
return nullptr;
}
return (vfp_sigframe*)&context->uc_regspace;
}
static bool ReadFPR64(CONTEXT* context, uint32_t vd, double* val) {
MOZ_ASSERT(vd < 32);
vfp_sigframe* frame = GetVFPFrame(context);
if (frame) {
*val = ((double*)frame->ufp.fpregs)[vd];
return true;
}
return false;
}
static bool WriteFPR64(CONTEXT* context, uint32_t vd, double val) {
MOZ_ASSERT(vd < 32);
vfp_sigframe* frame = GetVFPFrame(context);
if (frame) {
((double*)frame->ufp.fpregs)[vd] = val;
return true;
}
return false;
}
static bool ReadFPR32(CONTEXT* context, uint32_t vd, float* val) {
MOZ_ASSERT(vd < 32);
vfp_sigframe* frame = GetVFPFrame(context);
if (frame) {
*val = ((float*)frame->ufp.fpregs)[vd];
return true;
}
return false;
}
static bool WriteFPR32(CONTEXT* context, uint32_t vd, float val) {
MOZ_ASSERT(vd < 32);
vfp_sigframe* frame = GetVFPFrame(context);
if (frame) {
((float*)frame->ufp.fpregs)[vd] = val;
return true;
}
return false;
}
static bool HandleUnalignedTrap(CONTEXT* context, uint8_t* pc,
Instance* instance) {
MOZ_RELEASE_ASSERT(uintptr_t(pc) % 4 == 0);
uint32_t instr = *(uint32_t*)pc;
uint32_t masked = instr & 0x0F300E00;
bool isVLDR = masked == 0x0D100A00;
bool isVSTR = masked == 0x0D000A00;
if (!isVLDR && !isVSTR) {
# ifdef ANDROID
__android_log_print(ANDROID_LOG_ERROR, "WASM", "Bad SIGBUS instr %08x",
instr);
# endif
# ifdef DEBUG
MOZ_CRASH("Unexpected instruction");
# endif
return false;
}
bool isUnconditional = (instr >> 28) == 0xE;
bool isDouble = (instr & 0x00000100) != 0;
bool isAdd = (instr & 0x00800000) != 0;
uint32_t dBit = (instr >> 22) & 1;
uint32_t offs = (instr & 0xFF) << 2;
uint32_t rn = (instr >> 16) & 0xF;
MOZ_RELEASE_ASSERT(isUnconditional);
MOZ_RELEASE_ASSERT(rn != 15);
uint8_t* p = (uint8_t*)ReadGPR(context, rn) + (isAdd ? offs : -offs);
if (!instance->memoryAccessInBounds(
p, isDouble ? sizeof(double) : sizeof(float))) {
return false;
}
if (isDouble) {
uint32_t vd = ((instr >> 12) & 0xF) | (dBit << 4);
double val;
if (isVLDR) {
memcpy(&val, p, sizeof(val));
if (WriteFPR64(context, vd, val)) {
SetContextPC(context, pc + 4);
return true;
}
} else {
if (ReadFPR64(context, vd, &val)) {
memcpy(p, &val, sizeof(val));
SetContextPC(context, pc + 4);
return true;
}
}
} else {
uint32_t vd = ((instr >> 11) & (0xF << 1)) | dBit;
float val;
if (isVLDR) {
memcpy(&val, p, sizeof(val));
if (WriteFPR32(context, vd, val)) {
SetContextPC(context, pc + 4);
return true;
}
} else {
if (ReadFPR32(context, vd, &val)) {
memcpy(p, &val, sizeof(val));
SetContextPC(context, pc + 4);
return true;
}
}
}
# ifdef DEBUG
MOZ_CRASH(
"SIGBUS handler could not access FP register, incompatible kernel?");
# endif
return false;
}
#else
static bool HandleUnalignedTrap(CONTEXT* context, uint8_t* pc,
Instance* instance) {
return false;
}
#endif
static MOZ_MUST_USE bool HandleTrap(CONTEXT* context,
bool isUnalignedSignal = false,
JSContext* assertCx = nullptr) {
MOZ_ASSERT(sAlreadyHandlingTrap.get());
uint8_t* pc = ContextToPC(context);
const CodeSegment* codeSegment = LookupCodeSegment(pc);
if (!codeSegment || !codeSegment->isModule()) {
return false;
}
const ModuleSegment& segment = *codeSegment->asModule();
Trap trap;
BytecodeOffset bytecode;
if (!segment.code().lookupTrap(pc, &trap, &bytecode)) {
return false;
}
Instance* instance = ((Frame*)ContextToFP(context))->tls->instance;
MOZ_RELEASE_ASSERT(&instance->code() == &segment.code() ||
trap == Trap::IndirectCallBadSig);
if (isUnalignedSignal) {
if (trap != Trap::OutOfBounds) {
return false;
}
if (HandleUnalignedTrap(context, pc, instance)) {
return true;
}
}
JSContext* cx =
instance->realm()->runtimeFromAnyThread()->mainContextFromAnyThread();
MOZ_RELEASE_ASSERT(!assertCx || cx == assertCx);
jit::JitActivation* activation = cx->activation()->asJit();
activation->startWasmTrap(trap, bytecode.offset(), ToRegisterState(context));
SetContextPC(context, segment.trapCode());
return true;
}
#if defined(XP_WIN)
static const unsigned sThreadLocalArrayPointerIndex = 11;
#ifndef JS_ENABLE_UWP
static LONG WINAPI WasmTrapHandler(LPEXCEPTION_POINTERS exception) {
if (!NtCurrentTeb()->Reserved1[sThreadLocalArrayPointerIndex]) {
return EXCEPTION_CONTINUE_SEARCH;
}
if (sAlreadyHandlingTrap.get()) {
return EXCEPTION_CONTINUE_SEARCH;
}
AutoHandlingTrap aht;
EXCEPTION_RECORD* record = exception->ExceptionRecord;
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION) {
return EXCEPTION_CONTINUE_SEARCH;
}
if (!HandleTrap(exception->ContextRecord, false, TlsContext.get())) {
return EXCEPTION_CONTINUE_SEARCH;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
#endif
#elif defined(XP_DARWIN)
# pragma pack(4)
typedef struct {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
} Request__mach_exception_raise_t;
# pragma pack()
struct ExceptionRequest {
Request__mach_exception_raise_t body;
mach_msg_trailer_t trailer;
};
static bool HandleMachException(const ExceptionRequest& request) {
mach_port_t cxThread = request.body.thread.name;
CONTEXT context;
# if defined(__x86_64__)
unsigned int thread_state_count = x86_THREAD_STATE64_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE64_COUNT;
int thread_state = x86_THREAD_STATE64;
int float_state = x86_FLOAT_STATE64;
# elif defined(__i386__)
unsigned int thread_state_count = x86_THREAD_STATE_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE_COUNT;
int thread_state = x86_THREAD_STATE;
int float_state = x86_FLOAT_STATE;
# elif defined(__arm__)
unsigned int thread_state_count = ARM_THREAD_STATE_COUNT;
unsigned int float_state_count = ARM_NEON_STATE_COUNT;
int thread_state = ARM_THREAD_STATE;
int float_state = ARM_NEON_STATE;
# else
# error Unsupported architecture
# endif
kern_return_t kret;
kret = thread_get_state(cxThread, thread_state,
(thread_state_t)&context.thread, &thread_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
kret = thread_get_state(cxThread, float_state,
(thread_state_t)&context.float_, &float_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
if (request.body.exception != EXC_BAD_ACCESS &&
request.body.exception != EXC_BAD_INSTRUCTION) {
return false;
}
{
AutoNoteSingleThreadedRegion anstr;
AutoHandlingTrap aht;
if (!HandleTrap(&context)) {
return false;
}
}
kret = thread_set_state(cxThread, float_state,
(thread_state_t)&context.float_, float_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
kret = thread_set_state(cxThread, thread_state,
(thread_state_t)&context.thread, thread_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
return true;
}
static mach_port_t sMachDebugPort = MACH_PORT_NULL;
static void MachExceptionHandlerThread() {
static const unsigned EXCEPTION_MSG_ID = 2405;
while (true) {
ExceptionRequest request;
kern_return_t kret =
mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request),
sMachDebugPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kret != KERN_SUCCESS) {
fprintf(stderr, "MachExceptionHandlerThread: mach_msg failed with %d\n",
(int)kret);
MOZ_CRASH();
}
if (request.body.Head.msgh_id != EXCEPTION_MSG_ID) {
fprintf(stderr, "Unexpected msg header id %d\n",
(int)request.body.Head.msgh_bits);
MOZ_CRASH();
}
bool handled = HandleMachException(request);
kern_return_t replyCode = handled ? KERN_SUCCESS : KERN_FAILURE;
__Reply__exception_raise_t reply;
reply.Head.msgh_bits =
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.body.Head.msgh_bits), 0);
reply.Head.msgh_size = sizeof(reply);
reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
reply.Head.msgh_local_port = MACH_PORT_NULL;
reply.Head.msgh_id = request.body.Head.msgh_id + 100;
reply.NDR = NDR_record;
reply.RetCode = replyCode;
mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
}
#else
# ifdef __mips__
static const uint32_t kWasmTrapSignal = SIGFPE;
# else
static const uint32_t kWasmTrapSignal = SIGILL;
# endif
static struct sigaction sPrevSEGVHandler;
static struct sigaction sPrevSIGBUSHandler;
static struct sigaction sPrevWasmTrapHandler;
static void WasmTrapHandler(int signum, siginfo_t* info, void* context) {
if (!sAlreadyHandlingTrap.get()) {
AutoHandlingTrap aht;
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS ||
signum == kWasmTrapSignal);
if (HandleTrap((CONTEXT*)context, signum == SIGBUS, TlsContext.get())) {
return;
}
}
struct sigaction* previousSignal = nullptr;
switch (signum) {
case SIGSEGV:
previousSignal = &sPrevSEGVHandler;
break;
case SIGBUS:
previousSignal = &sPrevSIGBUSHandler;
break;
case kWasmTrapSignal:
previousSignal = &sPrevWasmTrapHandler;
break;
}
MOZ_ASSERT(previousSignal);
if (previousSignal->sa_flags & SA_SIGINFO) {
previousSignal->sa_sigaction(signum, info, context);
} else if (previousSignal->sa_handler == SIG_DFL ||
previousSignal->sa_handler == SIG_IGN) {
sigaction(signum, previousSignal, nullptr);
} else {
previousSignal->sa_handler(signum);
}
}
#endif
#if defined(ANDROID) && defined(MOZ_LINKER)
extern "C" MFBT_API bool IsSignalHandlingBroken();
#endif
struct InstallState {
bool tried;
bool success;
InstallState() : tried(false), success(false) {}
};
static ExclusiveData<InstallState> sEagerInstallState(
mutexid::WasmSignalInstallState);
void wasm::EnsureEagerProcessSignalHandlers() {
auto eagerInstallState = sEagerInstallState.lock();
if (eagerInstallState->tried) {
return;
}
eagerInstallState->tried = true;
MOZ_RELEASE_ASSERT(eagerInstallState->success == false);
#if defined(JS_CODEGEN_NONE)
return;
#endif
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
return;
}
#if defined(ANDROID) && defined(MOZ_LINKER)
if (IsSignalHandlingBroken()) {
return;
}
#endif
sAlreadyHandlingTrap.infallibleInit();
#if defined(XP_WIN)
# if defined(MOZ_ASAN)
const bool firstHandler = false;
# else
const bool firstHandler = true;
# endif
# ifndef JS_ENABLE_UWP
if (!AddVectoredExceptionHandler(firstHandler, WasmTrapHandler)) {
return;
}
# endif
#elif defined(XP_DARWIN)
#else
struct sigaction faultHandler;
faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
faultHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&faultHandler.sa_mask);
if (sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler)) {
MOZ_CRASH("unable to install segv handler");
}
# if defined(JS_CODEGEN_ARM)
struct sigaction busHandler;
busHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
busHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&busHandler.sa_mask);
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler)) {
MOZ_CRASH("unable to install sigbus handler");
}
# endif
struct sigaction wasmTrapHandler;
wasmTrapHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
wasmTrapHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&wasmTrapHandler.sa_mask);
if (sigaction(kWasmTrapSignal, &wasmTrapHandler, &sPrevWasmTrapHandler)) {
MOZ_CRASH("unable to install wasm trap handler");
}
#endif
eagerInstallState->success = true;
}
static ExclusiveData<InstallState> sLazyInstallState(
mutexid::WasmSignalInstallState);
static bool EnsureLazyProcessSignalHandlers() {
auto lazyInstallState = sLazyInstallState.lock();
if (lazyInstallState->tried) {
return lazyInstallState->success;
}
lazyInstallState->tried = true;
MOZ_RELEASE_ASSERT(lazyInstallState->success == false);
#ifdef XP_DARWIN
kern_return_t kret;
kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&sMachDebugPort);
if (kret != KERN_SUCCESS) {
return false;
}
kret = mach_port_insert_right(mach_task_self(), sMachDebugPort,
sMachDebugPort, MACH_MSG_TYPE_MAKE_SEND);
if (kret != KERN_SUCCESS) {
return false;
}
Thread handlerThread;
if (!handlerThread.init(MachExceptionHandlerThread)) {
return false;
}
handlerThread.detach();
#endif
lazyInstallState->success = true;
return true;
}
bool wasm::EnsureFullSignalHandlers(JSContext* cx) {
if (cx->wasmTriedToInstallSignalHandlers) {
return cx->wasmHaveSignalHandlers;
}
cx->wasmTriedToInstallSignalHandlers = true;
MOZ_RELEASE_ASSERT(!cx->wasmHaveSignalHandlers);
{
auto eagerInstallState = sEagerInstallState.lock();
MOZ_RELEASE_ASSERT(eagerInstallState->tried);
if (!eagerInstallState->success) {
return false;
}
}
if (!EnsureLazyProcessSignalHandlers()) {
return false;
}
#ifdef XP_DARWIN
MOZ_RELEASE_ASSERT(sMachDebugPort != MACH_PORT_NULL);
thread_port_t thisThread = mach_thread_self();
kern_return_t kret = thread_set_exception_ports(
thisThread, EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
sMachDebugPort, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
THREAD_STATE_NONE);
mach_port_deallocate(mach_task_self(), thisThread);
if (kret != KERN_SUCCESS) {
return false;
}
#endif
cx->wasmHaveSignalHandlers = true;
return true;
}
bool wasm::MemoryAccessTraps(const RegisterState& regs, uint8_t* addr,
uint32_t numBytes, uint8_t** newPC) {
const wasm::CodeSegment* codeSegment = wasm::LookupCodeSegment(regs.pc);
if (!codeSegment || !codeSegment->isModule()) {
return false;
}
const wasm::ModuleSegment& segment = *codeSegment->asModule();
Trap trap;
BytecodeOffset bytecode;
if (!segment.code().lookupTrap(regs.pc, &trap, &bytecode) ||
trap != Trap::OutOfBounds) {
return false;
}
Instance& instance = *reinterpret_cast<Frame*>(regs.fp)->tls->instance;
MOZ_ASSERT(&instance.code() == &segment.code());
if (!instance.memoryAccessInGuardRegion((uint8_t*)addr, numBytes)) {
return false;
}
jit::JitActivation* activation = TlsContext.get()->activation()->asJit();
activation->startWasmTrap(Trap::OutOfBounds, bytecode.offset(), regs);
*newPC = segment.trapCode();
return true;
}
bool wasm::HandleIllegalInstruction(const RegisterState& regs,
uint8_t** newPC) {
const wasm::CodeSegment* codeSegment = wasm::LookupCodeSegment(regs.pc);
if (!codeSegment || !codeSegment->isModule()) {
return false;
}
const wasm::ModuleSegment& segment = *codeSegment->asModule();
Trap trap;
BytecodeOffset bytecode;
if (!segment.code().lookupTrap(regs.pc, &trap, &bytecode)) {
return false;
}
jit::JitActivation* activation = TlsContext.get()->activation()->asJit();
activation->startWasmTrap(trap, bytecode.offset(), regs);
*newPC = segment.trapCode();
return true;
}
#undef WASM_EMULATE_ARM_UNALIGNED_FP_ACCESS