Index: src/nonsfi/linux/irt_exception_handling.c |
diff --git a/src/nonsfi/linux/irt_exception_handling.c b/src/nonsfi/linux/irt_exception_handling.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..752e674a1d58f0a1c496fe30c27b5e1a3c251a55 |
--- /dev/null |
+++ b/src/nonsfi/linux/irt_exception_handling.c |
@@ -0,0 +1,295 @@ |
+/* |
+ * Copyright (c) 2014 The Native Client Authors. All rights reserved. |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include <errno.h> |
+#include <pthread.h> |
+#include <signal.h> |
+#include <stdint.h> |
+#include <string.h> |
+#include <unistd.h> |
+ |
+#include "native_client/src/include/elf_constants.h" |
+#include "native_client/src/include/nacl/nacl_exception.h" |
+#include "native_client/src/include/nacl_macros.h" |
+#include "native_client/src/nonsfi/linux/linux_sys_private.h" |
+#include "native_client/src/nonsfi/linux/linux_syscall_defines.h" |
+#include "native_client/src/nonsfi/linux/linux_syscall_structs.h" |
+#include "native_client/src/public/nonsfi/irt_exception_handling.h" |
+#include "native_client/src/untrusted/irt/irt.h" |
+ |
+typedef struct compat_sigaltstack { |
+ uint32_t ss_sp; |
+ int32_t ss_flags; |
+ uint32_t ss_size; |
+} linux_stack_t; |
+ |
+#if defined(__i386__) |
+ |
+/* From linux/arch/x86/include/uapi/asm/sigcontext32.h */ |
+struct sigcontext_ia32 { |
+ unsigned short gs, __gsh; |
+ unsigned short fs, __fsh; |
+ unsigned short es, __esh; |
+ unsigned short ds, __dsh; |
+ unsigned int di; |
+ unsigned int si; |
+ unsigned int bp; |
+ unsigned int sp; |
+ unsigned int bx; |
+ unsigned int dx; |
+ unsigned int cx; |
+ unsigned int ax; |
+ unsigned int trapno; |
+ unsigned int err; |
+ unsigned int ip; |
+ unsigned short cs, __csh; |
+ unsigned int flags; |
+ unsigned int sp_at_signal; |
+ unsigned short ss, __ssh; |
+ unsigned int fpstate; |
+ unsigned int oldmask; |
+ unsigned int cr2; |
+}; |
+ |
+typedef struct sigcontext_ia32 linux_mcontext_t; |
+ |
+#elif defined(__arm__) |
+ |
+/* From linux/arch/arm/include/uapi/asm/sigcontext.h */ |
+struct sigcontext_arm { |
+ uint32_t trap_no; |
+ uint32_t error_code; |
+ uint32_t oldmask; |
+ uint32_t arm_r0; |
+ uint32_t arm_r1; |
+ uint32_t arm_r2; |
+ uint32_t arm_r3; |
+ uint32_t arm_r4; |
+ uint32_t arm_r5; |
+ uint32_t arm_r6; |
+ uint32_t arm_r7; |
+ uint32_t arm_r8; |
+ uint32_t arm_r9; |
+ uint32_t arm_r10; |
+ uint32_t arm_r11; /* fp */ |
+ uint32_t arm_r12; /* ip */ |
+ uint32_t arm_sp; |
+ uint32_t arm_lr; |
+ uint32_t arm_pc; |
+ uint32_t arm_cpsr; |
+ uint32_t fault_address; |
+}; |
+ |
+typedef struct sigcontext_arm linux_mcontext_t; |
+ |
+#else |
+#error "unsupported architecture" |
+#endif |
+ |
+/* From linux/arch/arm/include/asm/ucontext.h */ |
+struct linux_ucontext_t { |
+ uint32_t uc_flags; |
+ uint32_t uc_link; |
+ linux_stack_t uc_stack; |
+ linux_mcontext_t uc_mcontext; |
+ linux_sigset_t uc_sigmask; |
+ /* More data follows which we don't care about. */ |
+}; |
+ |
+/* |
+ * Crash signals to handle. The differences from SFI NaCl are that |
+ * NonSFI NaCl does not use NACL_THREAD_SUSPEND_SIGNAL (==SIGUSR1), |
+ */ |
+static const int kSignals[] = { |
+ LINUX_SIGSTKFLT, |
+ LINUX_SIGINT, LINUX_SIGQUIT, LINUX_SIGILL, LINUX_SIGTRAP, LINUX_SIGBUS, |
+ LINUX_SIGFPE, LINUX_SIGSEGV, |
+ /* Handle SIGABRT in case someone sends it asynchronously using kill(). */ |
+ LINUX_SIGABRT, |
+}; |
+ |
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; |
+static NaClExceptionHandler g_signal_handler_function_pointer = NULL; |
+static int g_signal_handler_initialized = 0; |
+ |
+struct NonSfiExceptionFrame { |
+ struct NaClExceptionContext context; |
+ struct NaClExceptionPortableContext portable; |
+}; |
+ |
+static void machine_context_to_register(const linux_mcontext_t *mctx, |
+ NaClUserRegisterState *dest) { |
+#if defined(__i386__) |
+#define COPY_REG(A) dest->e##A = mctx->A |
+ COPY_REG(ax); |
+ COPY_REG(cx); |
+ COPY_REG(dx); |
+ COPY_REG(bx); |
+ COPY_REG(bp); |
+ COPY_REG(si); |
+ COPY_REG(di); |
+#undef COPY_REG |
+ dest->stack_ptr = mctx->sp; |
+ dest->prog_ctr = mctx->ip; |
+ dest->flags = mctx->flags; |
+#elif defined(__arm__) |
+#define COPY_REG(A) dest->A = mctx->arm_##A |
+ COPY_REG(r0); |
+ COPY_REG(r1); |
+ COPY_REG(r2); |
+ COPY_REG(r3); |
+ COPY_REG(r4); |
+ COPY_REG(r5); |
+ COPY_REG(r6); |
+ COPY_REG(r7); |
+ COPY_REG(r8); |
+ COPY_REG(r9); |
+ COPY_REG(r10); |
+ COPY_REG(r11); |
+ COPY_REG(r12); |
+#undef COPY_REG |
+ dest->stack_ptr = mctx->arm_sp; |
+ dest->lr = mctx->arm_lr; |
+ dest->prog_ctr = mctx->arm_pc; |
+ dest->cpsr = mctx->arm_cpsr; |
+#else |
+# error Unsupported architecture |
+#endif |
+} |
+ |
+static void exception_frame_from_signal_context( |
+ struct NonSfiExceptionFrame *frame, |
+ const void *raw_ctx) { |
+ const struct linux_ucontext_t *uctx = (struct linux_ucontext_t *) raw_ctx; |
+ const linux_mcontext_t *mctx = &uctx->uc_mcontext; |
+ frame->context.size = (((uintptr_t) (&frame->portable + 1)) |
+ - (uintptr_t) &frame->context); |
+ frame->context.portable_context_offset = ((uintptr_t) &frame->portable |
+ - (uintptr_t) &frame->context); |
+ frame->context.portable_context_size = sizeof(frame->portable); |
+ frame->context.regs_size = sizeof(frame->context.regs); |
+ |
+ memset(frame->context.reserved, 0, sizeof(frame->context.reserved)); |
+ machine_context_to_register(mctx, &frame->context.regs); |
+ frame->portable.prog_ctr = frame->context.regs.prog_ctr; |
+ frame->portable.stack_ptr = frame->context.regs.stack_ptr; |
+ |
+#if defined(__i386__) |
+ frame->context.arch = EM_386; |
+ frame->portable.frame_ptr = frame->context.regs.ebp; |
+#elif defined(__arm__) |
+ frame->context.arch = EM_ARM; |
+ /* R11 is frame pointer in ARM mode, R8 is frame pointer in thumb mode. */ |
+ frame->portable.frame_ptr = frame->context.regs.r11; |
+#else |
+# error Unsupported architecture |
+#endif |
+} |
+ |
+/* Signal handler, responsible for calling the registered handler. */ |
+static void signal_catch(int sig, linux_siginfo_t *info, void *uc) { |
+ if (g_signal_handler_function_pointer) { |
+ struct NonSfiExceptionFrame exception_frame; |
+ exception_frame_from_signal_context(&exception_frame, uc); |
+ g_signal_handler_function_pointer(&exception_frame.context); |
+ } |
+ _exit(-sig); |
+} |
+ |
+static void nonsfi_initialize_signal_handler_locked() { |
+ struct linux_sigaction sa; |
+ unsigned int a; |
+ |
+ memset(&sa, 0, sizeof(sa)); |
+ sa.sa_sigaction = signal_catch; |
+ sa.sa_flags = LINUX_SA_SIGINFO | LINUX_SA_ONSTACK; |
+ |
+ /* |
+ * Reuse the sigemptyset/sigaddset for the first 32 bits of the |
+ * sigmask. Works on little endian systems only. |
+ */ |
+ sigset_t *mask = (sigset_t*)&sa.sa_mask; |
+ sigemptyset(mask); |
+ |
+ /* Mask all signals we catch to prevent re-entry. */ |
+ for (a = 0; a < NACL_ARRAY_SIZE(kSignals); a++) { |
+ sigaddset(mask, kSignals[a]); |
+ } |
+ |
+ /* Install all handlers. */ |
+ for (a = 0; a < NACL_ARRAY_SIZE(kSignals); a++) { |
+ if (linux_sigaction(kSignals[a], &sa, NULL) != 0) |
+ abort(); |
+ } |
+} |
+ |
+/* |
+ * Initialize signal handlers before entering sandbox. |
+ */ |
+void nonsfi_initialize_signal_handler() { |
+ if (pthread_mutex_lock(&g_mutex) != 0) |
+ abort(); |
+ if (!g_signal_handler_initialized) { |
+ nonsfi_initialize_signal_handler_locked(); |
+ g_signal_handler_initialized = 1; |
+ } |
+ if (pthread_mutex_unlock(&g_mutex) != 0) |
+ abort(); |
+} |
+ |
+int nacl_exception_get_and_set_handler(NaClExceptionHandler handler, |
+ NaClExceptionHandler *old_handler) { |
+ nonsfi_initialize_signal_handler(); |
+ if (pthread_mutex_lock(&g_mutex) != 0) |
+ abort(); |
+ if (old_handler) |
+ *old_handler = g_signal_handler_function_pointer; |
+ g_signal_handler_function_pointer = handler; |
+ if (pthread_mutex_unlock(&g_mutex) != 0) |
+ abort(); |
+ return 0; |
+} |
+ |
+int nacl_exception_set_handler(NaClExceptionHandler handler) { |
+ return nacl_exception_get_and_set_handler(handler, NULL); |
+} |
+ |
+int nacl_exception_clear_flag(void) { |
+ /* |
+ * Unblock signals, useful for unit testing and continuing to |
+ * process after fatal signal. |
+ */ |
+ |
+ /* Allocate the 8 bytes of signal mask. */ |
+ linux_sigset_t mask; |
+ |
+ /* |
+ * sigemptyset will only clear first 4 bytes of sigset_t, and |
+ * compat_sigset_t has 8 bytes, clear with memset. |
+ */ |
+ memset(&mask, 0, sizeof(mask)); |
+ |
+ /* |
+ * Hack to be able to reuse sigset_t utilities from newlib for the |
+ * first lower 4 bytes of the signal, works because we are all |
+ * little endians. |
+ */ |
+ sigset_t *maskptr = (sigset_t *) &mask; |
+ sigemptyset(maskptr); |
+ for (int a = 0; a < NACL_ARRAY_SIZE(kSignals); a++) { |
+ if (sigaddset(maskptr, kSignals[a]) != 0) |
+ abort(); |
+ } |
+ if (linux_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL) != 0) |
+ abort(); |
+ |
+ return 0; |
+} |
+ |
+int nacl_exception_set_stack(void *p, size_t s) { |
+ /* Not implemented yet. */ |
+ return ENOSYS; |
+} |