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