| Index: src/trusted/service_runtime/osx/mach_exception_handler.c
|
| ===================================================================
|
| --- src/trusted/service_runtime/osx/mach_exception_handler.c (revision 10857)
|
| +++ src/trusted/service_runtime/osx/mach_exception_handler.c (working copy)
|
| @@ -26,11 +26,13 @@
|
| #include "native_client/src/trusted/service_runtime/nacl_exception.h"
|
| #include "native_client/src/trusted/service_runtime/nacl_globals.h"
|
| #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
|
| +#include "native_client/src/trusted/service_runtime/nacl_tls.h"
|
| +#include "native_client/src/trusted/service_runtime/osx/crash_filter.h"
|
| +#include "native_client/src/trusted/service_runtime/osx/mach_thread_map.h"
|
| #include "native_client/src/trusted/service_runtime/sel_ldr.h"
|
| #include "native_client/src/trusted/service_runtime/sel_rt.h"
|
|
|
| -/* Only handle x86_32 for now. */
|
| -#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
|
| +#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
|
|
|
|
|
| /*
|
| @@ -87,90 +89,128 @@
|
| }
|
| }
|
|
|
| +#if NACL_BUILD_SUBARCH == 32
|
| +
|
| +#define NATIVE_x86_THREAD_STATE x86_THREAD_STATE32
|
| +#define X86_REG_BP(regs) ((regs).uts.ts32.__ebp)
|
| +#define X86_REG_SP(regs) ((regs).uts.ts32.__esp)
|
| +#define X86_REG_IP(regs) ((regs).uts.ts32.__eip)
|
| +#define X86_REG_FLAGS(regs) ((regs).uts.ts32.__eflags)
|
| +
|
| +#elif NACL_BUILD_SUBARCH == 64
|
| +
|
| +#define NATIVE_x86_THREAD_STATE x86_THREAD_STATE64
|
| +#define X86_REG_BP(regs) ((regs).uts.ts64.__rbp)
|
| +#define X86_REG_SP(regs) ((regs).uts.ts64.__rsp)
|
| +#define X86_REG_IP(regs) ((regs).uts.ts64.__rip)
|
| +#define X86_REG_FLAGS(regs) ((regs).uts.ts64.__rflags)
|
| +
|
| +#endif /* NACL_BUILD_SUBARCH */
|
| +
|
| static int HandleException(mach_port_t thread_port,
|
| exception_type_t exception, int *is_untrusted) {
|
| mach_msg_type_number_t size;
|
| x86_thread_state_t regs;
|
| kern_return_t result;
|
| - uint16_t trusted_cs = NaClGetGlobalCs();
|
| - uint16_t trusted_ds = NaClGetGlobalDs();
|
| uint32_t nacl_thread_index;
|
| struct NaClApp *nap;
|
| struct NaClAppThread *natp;
|
| struct NaClExceptionFrame frame;
|
| - uintptr_t frame_addr_user;
|
| + uint32_t frame_addr_user;
|
| uintptr_t frame_addr_sys;
|
| +#if NACL_BUILD_SUBARCH == 32
|
| + uint16_t trusted_cs = NaClGetGlobalCs();
|
| + uint16_t trusted_ds = NaClGetGlobalDs();
|
| +#endif
|
|
|
| /* Assume untrusted crash until we know otherwise. */
|
| *is_untrusted = TRUE;
|
|
|
| /* Capture the register state of the 'excepting' thread. */
|
| - size = sizeof(regs) / sizeof(natural_t);
|
| + size = x86_THREAD_STATE_COUNT;
|
| result = thread_get_state(thread_port, x86_THREAD_STATE,
|
| - (void *) ®s, &size);
|
| + (thread_state_t) ®s, &size);
|
| if (result != KERN_SUCCESS) {
|
| return 0;
|
| }
|
| + CHECK(regs.tsh.flavor == NATIVE_x86_THREAD_STATE);
|
|
|
| +#if NACL_BUILD_SUBARCH == 32
|
| /*
|
| - * If trusted_cs is 0 (which is not a usable segment selector), the
|
| - * sandbox has not been initialised yet, so there can be no untrusted
|
| - * code running.
|
| + * We can get the thread index from the segment selector used for TLS
|
| + * from %gs >> 3.
|
| + * TODO(bradnelson): Migrate that knowledge to a single shared location.
|
| */
|
| - if (trusted_cs == 0) {
|
| + nacl_thread_index = regs.uts.ts32.__gs >> 3;
|
| +#elif NACL_BUILD_SUBARCH == 64
|
| + nacl_thread_index = NaClGetThreadIndexForMachThread(thread_port);
|
| +
|
| + if (nacl_thread_index == NACL_TLS_INDEX_INVALID) {
|
| *is_untrusted = FALSE;
|
| return 0;
|
| }
|
| +#endif
|
|
|
| + natp = NaClAppThreadGetFromIndex(nacl_thread_index);
|
| + if (natp == NULL) {
|
| + *is_untrusted = FALSE;
|
| + return 0;
|
| + }
|
| + nap = natp->nap;
|
| +
|
| /*
|
| - * If the current code segment is the trusted one, we aren't in the
|
| - * sandbox.
|
| - * TODO(bradnelson): This makes the potentially false assumption that cs is
|
| - * the last thing to change when switching into untrusted code. We need
|
| - * tests to vet this.
|
| + * TODO(bradnelson): For x86_32, this makes the potentially false assumption
|
| + * that cs is the last thing to change when switching into untrusted
|
| + * code. We need tests to vet this.
|
| */
|
| - if (regs.uts.ts32.__cs == trusted_cs) {
|
| + *is_untrusted = NaClMachThreadStateIsInUntrusted(®s, nacl_thread_index);
|
| +
|
| + /*
|
| + * If trusted code accidentally jumped to untrusted code, don't let the
|
| + * untrusted exception handler take over.
|
| + */
|
| + if (*is_untrusted &&
|
| + (natp->suspend_state & NACL_APP_THREAD_UNTRUSTED) == 0) {
|
| + *is_untrusted = 0;
|
| + return 0;
|
| + }
|
| +
|
| + if (!*is_untrusted) {
|
| +#if NACL_BUILD_SUBARCH == 32
|
| /*
|
| * If we are single-stepping, allow NaClSwitchRemainingRegsViaECX()
|
| * to continue in order to restore control to untrusted code.
|
| */
|
| if (exception == EXC_BREAKPOINT &&
|
| - (regs.uts.ts32.__eflags & NACL_X86_TRAP_FLAG) != 0 &&
|
| - regs.uts.ts32.__eip >= (uintptr_t) NaClSwitchRemainingRegsViaECX &&
|
| - regs.uts.ts32.__eip < (uintptr_t) NaClSwitchRemainingRegsAsmEnd) {
|
| + (X86_REG_FLAGS(regs) & NACL_X86_TRAP_FLAG) != 0 &&
|
| + X86_REG_IP(regs) >= (uintptr_t) NaClSwitchRemainingRegsViaECX &&
|
| + X86_REG_IP(regs) < (uintptr_t) NaClSwitchRemainingRegsAsmEnd) {
|
| return 1;
|
| }
|
| - *is_untrusted = FALSE;
|
| +#endif
|
| return 0;
|
| }
|
|
|
| - /*
|
| - * We can get the thread index from the segment selector used for TLS
|
| - * from %gs >> 3.
|
| - * TODO(bradnelson): Migrate that knowledge to a single shared location.
|
| - */
|
| - nacl_thread_index = regs.uts.ts32.__gs >> 3;
|
| - natp = NaClAppThreadGetFromIndex(nacl_thread_index);
|
| - nap = natp->nap;
|
| -
|
| if (nap->enable_faulted_thread_queue) {
|
| +#if NACL_BUILD_SUBARCH == 32
|
| /*
|
| * If we are single-stepping, step through until we reach untrusted code.
|
| */
|
| if (exception == EXC_BREAKPOINT &&
|
| - (regs.uts.ts32.__eflags & NACL_X86_TRAP_FLAG) != 0) {
|
| - if (regs.uts.ts32.__eip >= nap->all_regs_springboard.start_addr &&
|
| - regs.uts.ts32.__eip < nap->all_regs_springboard.end_addr) {
|
| + (X86_REG_FLAGS(regs) & NACL_X86_TRAP_FLAG) != 0) {
|
| + if (X86_REG_IP(regs) >= nap->all_regs_springboard.start_addr &&
|
| + X86_REG_IP(regs) < nap->all_regs_springboard.end_addr) {
|
| return 1;
|
| }
|
| /*
|
| * Step through the instruction we have been asked to restore
|
| * control to.
|
| */
|
| - if (regs.uts.ts32.__eip == natp->user.gs_segment.new_prog_ctr) {
|
| + if (X86_REG_IP(regs) == natp->user.gs_segment.new_prog_ctr) {
|
| return 1;
|
| }
|
| }
|
| +#endif
|
|
|
| /*
|
| * Increment the kernel's thread suspension count so that the
|
| @@ -203,6 +243,11 @@
|
| return 0;
|
| }
|
|
|
| + /* Don't handle if no exception handler is set. */
|
| + if (nap->exception_handler == 0) {
|
| + return 0;
|
| + }
|
| +
|
| /* Don't handle it if the exception flag is set. */
|
| if (natp->exception_flag) {
|
| return 0;
|
| @@ -210,17 +255,12 @@
|
| /* Set the flag. */
|
| natp->exception_flag = 1;
|
|
|
| - /* Don't handle if no exception handler is set. */
|
| - if (nap->exception_handler == 0) {
|
| - return 0;
|
| - }
|
| -
|
| /* Get location of exception stack frame. */
|
| if (natp->exception_stack) {
|
| frame_addr_user = natp->exception_stack;
|
| } else {
|
| /* If not set default to user stack. */
|
| - frame_addr_user = regs.uts.ts32.__esp;
|
| + frame_addr_user = X86_REG_SP(regs) - NACL_STACK_RED_ZONE;
|
| }
|
|
|
| /* Align stack frame properly. */
|
| @@ -238,11 +278,13 @@
|
|
|
| /* Set up the stack frame for the handler invocation. */
|
| frame.return_addr = 0;
|
| + frame.context.prog_ctr = X86_REG_IP(regs);
|
| + frame.context.stack_ptr = X86_REG_SP(regs);
|
| + frame.context.frame_ptr = X86_REG_BP(regs);
|
| +#if NACL_BUILD_SUBARCH == 32
|
| frame.context_ptr = frame_addr_user +
|
| offsetof(struct NaClExceptionFrame, context);
|
| - frame.context.prog_ctr = regs.uts.ts32.__eip;
|
| - frame.context.stack_ptr = regs.uts.ts32.__esp;
|
| - frame.context.frame_ptr = regs.uts.ts32.__ebp;
|
| +#endif
|
|
|
| /*
|
| * Write the stack frame into untrusted address space. We do not
|
| @@ -259,10 +301,8 @@
|
| }
|
|
|
| /* Set up thread context to resume at handler. */
|
| - natp->user.new_prog_ctr = nap->exception_handler;
|
| - natp->user.stack_ptr = frame_addr_user;
|
| /* TODO(bradnelson): put all registers in some default state. */
|
| -
|
| +#if NACL_BUILD_SUBARCH == 32
|
| /*
|
| * Put registers in right place to land at NaClSwitchNoSSEViaECX
|
| * This is required because:
|
| @@ -276,15 +316,26 @@
|
| * Instead we call a variant of NaClSwitchNoSSE which takes a pointer
|
| * to the thread user context in %ecx.
|
| */
|
| - regs.uts.ts32.__eip = (uint32_t) &NaClSwitchNoSSEViaECX;
|
| + natp->user.new_prog_ctr = nap->exception_handler;
|
| + natp->user.stack_ptr = frame_addr_user;
|
| + X86_REG_IP(regs) = (uint32_t) &NaClSwitchNoSSEViaECX;
|
| regs.uts.ts32.__cs = trusted_cs;
|
| regs.uts.ts32.__ecx = (uint32_t) &natp->user;
|
| regs.uts.ts32.__ds = trusted_ds;
|
| regs.uts.ts32.__es = trusted_ds; /* just for good measure */
|
| regs.uts.ts32.__ss = trusted_ds; /* just for good measure */
|
| - regs.uts.ts32.__eflags &= ~NACL_X86_DIRECTION_FLAG;
|
| +#elif NACL_BUILD_SUBARCH == 64
|
| + X86_REG_IP(regs) = NaClUserToSys(nap, nap->exception_handler);
|
| + X86_REG_SP(regs) = frame_addr_sys;
|
| + X86_REG_BP(regs) = nap->mem_start;
|
| +
|
| + /* Argument 1 */
|
| + regs.uts.ts64.__rdi = frame_addr_user +
|
| + offsetof(struct NaClExceptionFrame, context);
|
| +#endif
|
| + X86_REG_FLAGS(regs) &= ~NACL_X86_DIRECTION_FLAG;
|
| result = thread_set_state(thread_port, x86_THREAD_STATE,
|
| - (void *) ®s, size);
|
| + (thread_state_t) ®s, size);
|
| if (result != KERN_SUCCESS) {
|
| return 0;
|
| }
|
| @@ -570,10 +621,4 @@
|
| return FALSE;
|
| }
|
|
|
| -#else /* NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32 */
|
| -
|
| -int NaClInterceptMachExceptions(void) {
|
| - return FALSE;
|
| -}
|
| -
|
| -#endif /* NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32 */
|
| +#endif /* NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 */
|
|
|