Chromium Code Reviews| Index: src/nonsfi/linux/irt_signal_handling.c |
| diff --git a/src/nonsfi/linux/irt_signal_handling.c b/src/nonsfi/linux/irt_signal_handling.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0953ead52b8a7bbfd3f65201f65c4048c564680e |
| --- /dev/null |
| +++ b/src/nonsfi/linux/irt_signal_handling.c |
| @@ -0,0 +1,289 @@ |
| +/* |
| + * Copyright (c) 2015 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/irt_signal_handling.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/linux_syscalls/sys/syscall.h" |
| +#include "native_client/src/public/nonsfi/irt_signal_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; |
| + |
| + |
| +/* 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. */ |
| +}; |
| + |
| +pthread_mutex_t g_signal_handler_mutex = PTHREAD_MUTEX_INITIALIZER; |
| +static NaClExceptionHandler g_signal_handler_function_pointer = NULL; |
| +static int g_signal_handler_initialized = 0; |
| +static int g_tgid = 0; |
| +static int g_main_tid; |
| + |
| +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 |
| +} |
| + |
| +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 |
| +} |
| + |
| +/* A replacement of sigreturn. It does not restore the signal mask. */ |
| +static void __attribute__((noreturn)) |
| +_nonsfi_restore_context(const linux_mcontext_t *mctx) { |
|
Junichi Uekawa
2015/07/01 02:14:57
this is not python code, don't prepend function na
Luis Héctor Chávez
2015/07/06 23:44:59
Done.
|
| + |
| +#if defined(__i386__) |
| + |
| +#define OFFSET(name) \ |
| + [name] "i" (offsetof(linux_mcontext_t, name)) |
| +#define RESTORE_SEGMENT(name) \ |
| + "mov %c[" #name "](%%eax), %%" #name "\n" |
| +#define RESTORE(name) \ |
| + "movl %c[" #name "](%%eax), %%e" #name "\n" |
| + |
| + __asm__ __volatile__( |
| + "mov %0, %%eax\n" |
| + |
| + /* Restore floating-point environment */ |
| + "mov %c[fpstate](%%eax), %%ecx\n" |
| + "fldenv (%%ecx)\n" |
| + |
| + /* Restore all segment registers */ |
| + RESTORE_SEGMENT(gs) |
| + RESTORE_SEGMENT(fs) |
| + RESTORE_SEGMENT(es) |
| + RESTORE_SEGMENT(ds) |
| + |
| + /* |
| + * Restore the rest of the registers except for dx, cx (scratch) and ax. |
| + */ |
| + RESTORE(di) |
| + RESTORE(si) |
| + RESTORE(bp) |
| + RESTORE(bx) |
| + |
| + /* |
| + * Prepare the last registers. eip, eflags, eax, and ecx should be on the |
| + * top of the new stack. Once that is done and esp restored, we can |
| + * safely restore them. |
| + */ |
| + "mov %c[sp](%%eax), %%ecx\n" |
| + "mov %c[ip](%%eax), %%edx\n" |
| + "mov %%edx, -4(%%ecx)\n" |
| + "mov %c[flags](%%eax), %%edx\n" |
| + "mov %%edx, -8(%%ecx)\n" |
| + "mov %c[ax](%%eax), %%edx\n" |
| + "mov %%edx, -12(%%ecx)\n" |
| + "mov %c[cx](%%eax), %%edx\n" |
| + "mov %%edx, -16(%%ecx)\n" |
| + RESTORE(dx) |
| + "sub $16, %%ecx\n" |
| + "mov %%ecx, %%esp\n" |
| + |
| + /* |
| + * Finally pop the last registers off the stack and return to |
| + * simultaneously restore esp and eip. |
| + */ |
| + "pop %%ecx\n" |
| + "pop %%eax\n" |
| + "popfd\n" |
| + "ret\n" |
| + : |
| + : "X" (mctx), |
| + OFFSET(gs), |
| + OFFSET(fs), |
| + OFFSET(es), |
| + OFFSET(ds), |
| + OFFSET(di), |
| + OFFSET(si), |
| + OFFSET(bp), |
| + OFFSET(sp), |
| + OFFSET(bx), |
| + OFFSET(dx), |
| + OFFSET(cx), |
| + OFFSET(ax), |
| + OFFSET(ip), |
| + OFFSET(flags), |
| + OFFSET(fpstate) |
| + ); |
| + |
| +#undef OFFSET |
| +#undef RESTORE |
| +#undef RESTORE_SEGMENT |
| + |
| +#elif defined(__arm__) |
| + |
| + /* TODO(lhchavez): Implement this. */ |
|
Junichi Uekawa
2015/07/01 02:14:57
erm... is this required ?
Luis Héctor Chávez
2015/07/06 23:44:59
It is. I wanted to have feedback on the rest of th
|
| + |
| +#else |
| +# error Unsupported architecture |
| +#endif |
| + |
| + /* Should never reach this. */ |
| + abort(); |
| +} |
| + |
| +static __attribute__((noreturn)) |
| +void restore_context(void *raw_ctx) { |
| + const struct linux_ucontext_t *uctx = (struct linux_ucontext_t *) raw_ctx; |
| + const linux_mcontext_t *mctx = &uctx->uc_mcontext; |
| + _nonsfi_restore_context(mctx); |
| +} |
| + |
| +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); |
| + } |
| + restore_context(uc); |
| +} |
| + |
| +static void nonsfi_install_signal_handler_locked() { |
| + struct linux_sigaction sa; |
| + |
| + memset(&sa, 0, sizeof(sa)); |
| + sa.sa_sigaction = signal_catch; |
| + |
| + /* |
| + * User signal handler can be recursively interrupted to avoid having |
| + * to allow sigreturn/sigprocmask. |
| + */ |
| + sa.sa_flags = LINUX_SA_SIGINFO | LINUX_SA_NODEFER; |
| + sigset_t *mask = (sigset_t*)&sa.sa_mask; |
| + sigemptyset(mask); |
| + |
| + /* |
| + * Install a single handler. Multiple signals can be multiplexed in |
| + * userspace. |
| + */ |
| + if (linux_sigaction(LINUX_SIGUSR1, &sa, NULL) != 0) |
| + abort(); |
| +} |
| + |
| +void nonsfi_initialize_signal_handler_locked() { |
| + if (g_signal_handler_initialized) |
| + return; |
| + nonsfi_install_exception_handler_locked(); |
| + nonsfi_install_signal_handler_locked(); |
| + g_tgid = getpid(); |
| + g_main_tid = syscall(__NR_gettid); |
| + g_signal_handler_initialized = 1; |
| +} |
| + |
| +/* |
| + * Initialize signal handlers before entering sandbox. |
| + */ |
| +void nonsfi_initialize_signal_handler() { |
| + if (pthread_mutex_lock(&g_signal_handler_mutex) != 0) |
| + abort(); |
| + nonsfi_initialize_signal_handler_locked(); |
| + if (pthread_mutex_unlock(&g_signal_handler_mutex) != 0) |
| + abort(); |
| +} |
| + |
| +int nacl_signal_set_handler(NaClIrtSignalHandler handler) { |
| + if (pthread_mutex_lock(&g_signal_handler_mutex) != 0) |
| + abort(); |
| + nonsfi_initialize_signal_handler_locked(); |
| + g_signal_handler_function_pointer = handler; |
| + if (pthread_mutex_unlock(&g_signal_handler_mutex) != 0) |
| + abort(); |
| + return 0; |
| +} |
| + |
| +int nacl_signal_send_async_signal(nacl_irt_tid_t tid) { |
| + if (!g_signal_handler_initialized) |
| + return ESRCH; |
| + if (tid == 0) |
| + tid = g_main_tid; |
| + if (linux_tgkill(g_tgid, tid, LINUX_SIGUSR1) == -1) |
| + return errno; |
| + return 0; |
| +} |