1 /*
2 * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <errno.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <sys/mman.h>
11
12 #include "native_client/src/include/build_config.h"
13 #include "native_client/src/shared/platform/nacl_check.h"
14 #include "native_client/src/shared/platform/nacl_log.h"
15 #include "native_client/src/trusted/service_runtime/nacl_config.h"
16 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
17
18
19 /*
20 * NaCl uses POSIX signal handling on Linux, but not on Mac OS X. We
21 * register a signal stack on Mac OS X anyway, though, for safety, to
22 * prevent a signal handler from running on an untrusted stack just in
23 * case a signal handler gets left registered.
24 */
25
26 /* Use 4K more than the minimum to allow breakpad to run. */
27 static uint32_t g_signal_stack_size = SIGSTKSZ + 4096;
28
29 #define STACK_GUARD_SIZE NACL_PAGESIZE
30
NaClSignalStackSetSize(uint32_t size)31 void NaClSignalStackSetSize(uint32_t size) {
32 g_signal_stack_size = size;
33 }
34
NaClSignalStackAllocate(void ** result)35 int NaClSignalStackAllocate(void **result) {
36 /*
37 * We use mmap() to allocate the signal stack for two reasons:
38 *
39 * 1) By page-aligning the memory allocation (which malloc() does
40 * not do for small allocations), we avoid allocating any real
41 * memory in the common case in which the signal handler is never
42 * run.
43 *
44 * 2) We get to create a guard page, to guard against the unlikely
45 * occurrence of the signal handler both overrunning and doing so in
46 * an exploitable way.
47 */
48 uint8_t *stack = mmap(NULL, g_signal_stack_size + STACK_GUARD_SIZE,
49 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
50 -1, 0);
51 if (stack == MAP_FAILED) {
52 return 0;
53 }
54 /* We assume that the stack grows downwards. */
55 if (mprotect(stack, STACK_GUARD_SIZE, PROT_NONE) != 0) {
56 NaClLog(LOG_FATAL, "Failed to mprotect() the stack guard page:\n\t%s\n",
57 strerror(errno));
58 }
59 *result = stack;
60 return 1;
61 }
62
NaClSignalStackFree(void * stack)63 void NaClSignalStackFree(void *stack) {
64 CHECK(stack != NULL);
65 if (munmap(stack, g_signal_stack_size + STACK_GUARD_SIZE) != 0) {
66 NaClLog(LOG_FATAL, "Failed to munmap() signal stack:\n\t%s\n",
67 strerror(errno));
68 }
69 }
70
NaClSignalStackRegister(void * stack)71 void NaClSignalStackRegister(void *stack) {
72 /*
73 * If we set up signal handlers, we must ensure that any thread that
74 * runs untrusted code has an alternate signal stack set up. The
75 * default for a new thread is to use the stack pointer from the
76 * point at which the fault occurs, but it would not be safe to use
77 * untrusted code's %esp/%rsp value.
78 */
79 stack_t st;
80 st.ss_size = g_signal_stack_size;
81 st.ss_sp = ((uint8_t *) stack) + STACK_GUARD_SIZE;
82 st.ss_flags = 0;
83 if (sigaltstack(&st, NULL) != 0) {
84 NaClLog(LOG_FATAL, "Failed to register signal stack:\n\t%s\n",
85 strerror(errno));
86 }
87 }
88
NaClSignalStackUnregister(void)89 void NaClSignalStackUnregister(void) {
90 /*
91 * Unregister the signal stack in case a fault occurs between the
92 * thread deallocating the signal stack and exiting. Such a fault
93 * could be unsafe if the address space were reallocated before the
94 * fault, although that is unlikely.
95 */
96 stack_t st;
97 #if NACL_OSX
98 /*
99 * This is a workaround for a bug in Mac OS X's libc, in which new
100 * versions of the sigaltstack() wrapper return ENOMEM if ss_size is
101 * less than MINSIGSTKSZ, even when ss_size should be ignored
102 * because we are unregistering the signal stack.
103 * See http://code.google.com/p/nativeclient/issues/detail?id=1053
104 */
105 st.ss_size = MINSIGSTKSZ;
106 #else
107 st.ss_size = 0;
108 #endif
109 st.ss_sp = NULL;
110 st.ss_flags = SS_DISABLE;
111 if (sigaltstack(&st, NULL) != 0) {
112 NaClLog(LOG_FATAL, "Failed to unregister signal stack:\n\t%s\n",
113 strerror(errno));
114 }
115 }
116