1 : /*
2 : * Copyright (c) 2012 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 <stdio.h>
8 : #include <stdlib.h>
9 : #include <string.h>
10 :
11 : #include "native_client/src/include/nacl_base.h"
12 : #include "native_client/src/include/portability_io.h"
13 : #include "native_client/src/shared/platform/nacl_check.h"
14 : #include "native_client/src/shared/platform/nacl_exit.h"
15 : #include "native_client/src/shared/platform/nacl_log.h"
16 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
17 : #include "native_client/src/trusted/service_runtime/nacl_globals.h"
18 : #include "native_client/src/trusted/service_runtime/nacl_signal.h"
19 : #include "native_client/src/trusted/service_runtime/nacl_tls.h"
20 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
21 :
22 : #ifdef WIN32
23 : #include <io.h>
24 : #define write _write
25 : #else
26 : #include <unistd.h>
27 : #endif
28 :
29 : #define MAX_NACL_HANDLERS 16
30 :
31 : struct NaClSignalNode {
32 : struct NaClSignalNode *next;
33 : NaClSignalHandler func;
34 : int id;
35 : };
36 :
37 :
38 : static struct NaClSignalNode *s_FirstHandler = NULL;
39 : static struct NaClSignalNode *s_FreeList = NULL;
40 : static struct NaClSignalNode s_SignalNodes[MAX_NACL_HANDLERS];
41 :
42 3 : ssize_t NaClSignalErrorMessage(const char *msg) {
43 : /*
44 : * We cannot use NaClLog() in the context of a signal handler: it is
45 : * too complex. However, write() is signal-safe.
46 : */
47 3 : size_t len_t = strlen(msg);
48 3 : int len = (int) len_t;
49 :
50 : /*
51 : * Write uses int not size_t, so we may wrap the length and/or
52 : * generate a negative value. Only print if it matches.
53 : */
54 3 : if ((len > 0) && (len_t == (size_t) len)) {
55 3 : return (ssize_t) write(2, msg, len);
56 : }
57 :
58 0 : return 0;
59 : }
60 :
61 : /*
62 : * Returns (via is_untrusted) whether the signal happened while
63 : * executing untrusted code. If the signal was from untrusted code,
64 : * this function also returns (via result_thread) the NaClAppThread
65 : * that untrusted code was running in; otherwise *result_thread is
66 : * undefined.
67 : *
68 : * Note that this should only be called from the thread in which the
69 : * signal occurred, because on x86-64 it reads a thread-local variable
70 : * (nacl_thread_index).
71 : */
72 : void NaClSignalContextGetCurrentThread(const struct NaClSignalContext *sigCtx,
73 : int *is_untrusted,
74 4 : struct NaClAppThread **result_thread) {
75 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
76 : /* For x86-32, if %cs does not match, it is untrusted code. */
77 4 : *is_untrusted = (NaClGetGlobalCs() != sigCtx->cs);
78 4 : if (*is_untrusted) {
79 0 : *result_thread = nacl_thread[sigCtx->gs >> 3];
80 : }
81 : #elif (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64) || \
82 : NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
83 : uint32_t current_thread_index = NaClTlsGetIdx();
84 : if (NACL_TLS_INDEX_INVALID == current_thread_index) {
85 : *is_untrusted = 0;
86 : } else {
87 : struct NaClAppThread *thread = nacl_thread[current_thread_index];
88 : /*
89 : * Get the address of an arbitrary local, stack-allocated variable,
90 : * just for the purpose of doing a sanity check.
91 : */
92 : void *pointer_into_stack = &thread;
93 : /*
94 : * Sanity check: Make sure the stack we are running on is not
95 : * allocated in untrusted memory. This checks that the alternate
96 : * signal stack is correctly set up, because otherwise, if it is
97 : * not set up, the test case would not detect that.
98 : *
99 : * There is little point in doing a CHECK instead of a DCHECK,
100 : * because if we are running off an untrusted stack, we have already
101 : * lost.
102 : *
103 : * We do not do the check on Windows because Windows does not have
104 : * an equivalent of sigaltstack() and this signal handler is
105 : * insecure there.
106 : */
107 : if (!NACL_WINDOWS) {
108 : DCHECK(!NaClIsUserAddr(thread->nap, (uintptr_t) pointer_into_stack));
109 : }
110 : *is_untrusted = NaClIsUserAddr(thread->nap, sigCtx->prog_ctr);
111 : *result_thread = thread;
112 : }
113 : #else
114 : # error Unsupported architecture
115 : #endif
116 4 : }
117 :
118 : /*
119 : * Returns whether the signal happened while executing untrusted code.
120 : *
121 : * Like NaClSignalContextGetCurrentThread(), this should only be
122 : * called from the thread in which the signal occurred.
123 : */
124 2 : int NaClSignalContextIsUntrusted(const struct NaClSignalContext *sigCtx) {
125 : struct NaClAppThread *thread_unused;
126 : int is_untrusted;
127 2 : NaClSignalContextGetCurrentThread(sigCtx, &is_untrusted, &thread_unused);
128 2 : return is_untrusted;
129 : }
130 :
131 0 : enum NaClSignalResult NaClSignalHandleNone(int signal, void *ctx) {
132 : UNREFERENCED_PARAMETER(signal);
133 : UNREFERENCED_PARAMETER(ctx);
134 :
135 : /* Don't do anything, just pass it to the OS. */
136 0 : return NACL_SIGNAL_SKIP;
137 : }
138 :
139 2 : enum NaClSignalResult NaClSignalHandleAll(int signal, void *ctx) {
140 : struct NaClSignalContext sigCtx;
141 : char tmp[128];
142 :
143 : /*
144 : * Return an 8 bit error code which is -signal to
145 : * simulate normal OS behavior
146 : */
147 :
148 2 : NaClSignalContextFromHandler(&sigCtx, ctx);
149 2 : if (NaClSignalContextIsUntrusted(&sigCtx)) {
150 0 : SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from untrusted code: Halting "
151 : "at %" NACL_PRIXNACL_REG "h\n", signal, sigCtx.prog_ctr);
152 0 : NaClSignalErrorMessage(tmp);
153 0 : NaClExit((-signal) & 0xFF);
154 : }
155 : else {
156 2 : SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from trusted code: Halting "
157 : "at %" NACL_PRIXNACL_REG "h\n", signal, sigCtx.prog_ctr);
158 2 : NaClSignalErrorMessage(tmp);
159 2 : NaClExit((-signal) & 0xFF);
160 : }
161 0 : return NACL_SIGNAL_RETURN;
162 : }
163 :
164 0 : enum NaClSignalResult NaClSignalHandleUntrusted(int signal, void *ctx) {
165 : struct NaClSignalContext sigCtx;
166 : char tmp[128];
167 : /*
168 : * Return an 8 bit error code which is -signal to
169 : * simulate normal OS behavior
170 : */
171 0 : NaClSignalContextFromHandler(&sigCtx, ctx);
172 0 : if (NaClSignalContextIsUntrusted(&sigCtx)) {
173 0 : SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from untrusted code: Halting "
174 : "at %" NACL_PRIXNACL_REG "h\n", signal, sigCtx.prog_ctr);
175 0 : NaClSignalErrorMessage(tmp);
176 0 : NaClExit((-signal) & 0xFF);
177 : }
178 : else {
179 0 : SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from trusted code: Continuing "
180 : "from %" NACL_PRIXNACL_REG "h\n", signal, sigCtx.prog_ctr);
181 0 : NaClSignalErrorMessage(tmp);
182 : }
183 0 : return NACL_SIGNAL_SEARCH;
184 : }
185 :
186 :
187 14 : int NaClSignalHandlerAdd(NaClSignalHandler func) {
188 14 : int id = 0;
189 :
190 14 : CHECK(func != NULL);
191 :
192 : /* If we have room... */
193 14 : if (s_FreeList) {
194 : /* Update the free list. */
195 14 : struct NaClSignalNode *add = s_FreeList;
196 14 : s_FreeList = add->next;
197 :
198 : /* Construct the node. */
199 14 : add->func = func;
200 14 : add->next = s_FirstHandler;
201 :
202 : /* Add node to the head. */
203 14 : s_FirstHandler = add;
204 14 : id = add->id;
205 : }
206 :
207 14 : return id;
208 : }
209 :
210 :
211 0 : int NaClSignalHandlerRemove(int id) {
212 : /* The first node pointer is the first "next" pointer. */
213 0 : struct NaClSignalNode **ppNode = &s_FirstHandler;
214 :
215 : /* While the "next" pointer is valid, process what it points to. */
216 0 : while (*ppNode) {
217 : /* If the next item has a matching ID */
218 0 : if ((*ppNode)->id == id) {
219 : /* then we will free that item. */
220 0 : struct NaClSignalNode *freeNode = *ppNode;
221 :
222 : /* First, skip past it. */
223 0 : *ppNode = (*ppNode)->next;
224 :
225 : /* Then add this node to the head of the free list. */
226 0 : freeNode->next = s_FreeList;
227 0 : s_FreeList = freeNode;
228 0 : return 1;
229 : }
230 0 : ppNode = &(*ppNode)->next;
231 : }
232 :
233 0 : return 0;
234 : }
235 :
236 2 : enum NaClSignalResult NaClSignalHandlerFind(int signal, void *ctx) {
237 2 : enum NaClSignalResult result = NACL_SIGNAL_SEARCH;
238 : struct NaClSignalNode *pNode;
239 :
240 : /* Iterate through handlers */
241 2 : pNode = s_FirstHandler;
242 4 : while (pNode) {
243 2 : result = pNode->func(signal, ctx);
244 :
245 : /* If we are not asking for the search to continue... */
246 0 : if (NACL_SIGNAL_SEARCH != result) break;
247 :
248 0 : pNode = pNode->next;
249 : }
250 :
251 0 : return result;
252 : }
253 :
254 14 : void NaClSignalHandlerInit() {
255 : int a;
256 :
257 : /* Build the free list */
258 238 : for (a = 0; a < MAX_NACL_HANDLERS; a++) {
259 224 : s_SignalNodes[a].next = s_FreeList;
260 224 : s_SignalNodes[a].id = a + 1;
261 224 : s_FreeList = &s_SignalNodes[a];
262 : }
263 :
264 14 : NaClSignalHandlerInitPlatform();
265 : #ifdef NACL_STANDALONE
266 : /* In stand-alone mode (sel_ldr) we handle all signals. */
267 14 : NaClSignalHandlerAdd(NaClSignalHandleAll);
268 : #else
269 : /*
270 : * When run in Chrome we handle only signals in untrusted code.
271 : * Signals in trusted code are allowed to pass back to Chrome so
272 : * that Breakpad can create a minidump when applicable.
273 : */
274 : NaClSignalHandlerAdd(NaClSignalHandleUntrusted);
275 : #endif
276 14 : if (getenv("NACL_CRASH_TEST") != NULL) {
277 1 : NaClSignalErrorMessage("[CRASH_TEST] Causing crash in NaCl "
278 : "trusted code...\n");
279 : /*
280 : * Clang transmutes a NULL pointer reference into a generic "undefined"
281 : * case. That code crashes with a different signal than an actual bad
282 : * pointer reference, violating the tests' expectations. A pointer that
283 : * is known bad but is not literally NULL does not get this treatment.
284 : */
285 0 : *(volatile int *) 1 = 0;
286 : }
287 13 : }
288 :
289 12 : void NaClSignalHandlerFini() {
290 : /* We try to lock, but since we are shutting down, we ignore failures. */
291 12 : NaClSignalHandlerFiniPlatform();
292 12 : }
|