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/build_config.h"
12 #include "native_client/src/include/nacl_base.h"
13 #include "native_client/src/include/portability_io.h"
14 #include "native_client/src/shared/platform/nacl_check.h"
15 #include "native_client/src/shared/platform/nacl_exit.h"
16 #include "native_client/src/shared/platform/nacl_log.h"
17 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
18 #include "native_client/src/trusted/service_runtime/nacl_exception.h"
19 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
20 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
21 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
22
23 #if NACL_WINDOWS
24 #include <io.h>
25 #define write _write
26 #else
27 #include <unistd.h>
28 #endif
29
30
NaClSignalErrorMessage(const char * msg)31 ssize_t NaClSignalErrorMessage(const char *msg) {
32 /*
33 * We cannot use NaClLog() in the context of a signal handler: it is
34 * too complex. However, write() is signal-safe.
35 */
36 size_t len_t = strlen(msg);
37 int len = (int) len_t;
38
39 /*
40 * Write uses int not size_t, so we may wrap the length and/or
41 * generate a negative value. Only print if it matches.
42 */
43 if ((len > 0) && (len_t == (size_t) len)) {
44 return (ssize_t) write(2, msg, len);
45 }
46
47 return 0;
48 }
49
50 /*
51 * This function takes the register state (sig_ctx) for a thread
52 * (natp) that has been suspended and returns whether the thread was
53 * suspended while executing untrusted code.
54 */
NaClSignalContextIsUntrusted(struct NaClAppThread * natp,const struct NaClSignalContext * sig_ctx)55 int NaClSignalContextIsUntrusted(struct NaClAppThread *natp,
56 const struct NaClSignalContext *sig_ctx) {
57 uint32_t prog_ctr;
58
59 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
60 /*
61 * Note that we do not check "sig_ctx != NaClGetGlobalCs()". On Mac
62 * OS X, if a thread is suspended while in a syscall,
63 * thread_get_state() returns cs=0x7 rather than cs=0x17 (the normal
64 * cs value for trusted code).
65 */
66 if (sig_ctx->cs != natp->user.cs)
67 return 0;
68 #elif (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64) || \
69 NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || \
70 NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
71 if (!NaClIsUserAddr(natp->nap, sig_ctx->prog_ctr))
72 return 0;
73 #else
74 # error Unsupported architecture
75 #endif
76
77 prog_ctr = (uint32_t) sig_ctx->prog_ctr;
78 return (prog_ctr < NACL_TRAMPOLINE_START ||
79 prog_ctr >= NACL_TRAMPOLINE_END);
80 }
81
82 /*
83 * Sanity checks: Reject unsafe register state that untrusted code
84 * should not be able to set unless there is a sandbox hole. We do
85 * this in an attempt to prevent such a hole from being exploitable.
86 */
NaClSignalCheckSandboxInvariants(const struct NaClSignalContext * regs,struct NaClAppThread * natp)87 int NaClSignalCheckSandboxInvariants(const struct NaClSignalContext *regs,
88 struct NaClAppThread *natp) {
89 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
90 if (regs->cs != natp->user.cs ||
91 regs->ds != natp->user.ds ||
92 regs->es != natp->user.es ||
93 regs->fs != natp->user.fs ||
94 regs->gs != natp->user.gs ||
95 regs->ss != natp->user.ss) {
96 return 0;
97 }
98 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
99 /*
100 * Untrusted code can temporarily set %rsp/%rbp to be in the 0..4GB
101 * range but it should not be able to generate a fault while in that
102 * state.
103 */
104 if (regs->r15 != natp->user.r15 ||
105 !NaClIsUserAddr(natp->nap, regs->stack_ptr) ||
106 !NaClIsUserAddr(natp->nap, regs->rbp)) {
107 return 0;
108 }
109 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
110 if (!NaClIsUserAddr(natp->nap, regs->stack_ptr) ||
111 regs->r9 != (uintptr_t) &natp->user.tls_value1) {
112 return 0;
113 }
114 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
115 if (!NaClIsUserAddr(natp->nap, regs->stack_ptr) ||
116 regs->t6 != NACL_CONTROL_FLOW_MASK ||
117 regs->t7 != NACL_DATA_FLOW_MASK) {
118 return 0;
119 }
120 #else
121 # error Unsupported architecture
122 #endif
123 return 1;
124 }
125
NaClSignalHandleUntrusted(int signal,const struct NaClSignalContext * regs,int is_untrusted)126 void NaClSignalHandleUntrusted(int signal,
127 const struct NaClSignalContext *regs,
128 int is_untrusted) {
129 char tmp[128];
130 /*
131 * Return an 8 bit error code which is -signal to
132 * simulate normal OS behavior
133 */
134 if (is_untrusted) {
135 SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from untrusted code: "
136 "pc=%" NACL_PRIxNACL_REG "\n", signal, regs->prog_ctr);
137 NaClSignalErrorMessage(tmp);
138 NaClExit((-signal) & 0xFF);
139 } else {
140 SNPRINTF(tmp, sizeof(tmp), "\n** Signal %d from trusted code: "
141 "pc=%" NACL_PRIxNACL_REG "\n", signal, regs->prog_ctr);
142 NaClSignalErrorMessage(tmp);
143 /*
144 * Continue the search for another handler so that trusted crashes
145 * can be handled by the Breakpad crash reporter.
146 */
147 }
148 }
149
150
151 /*
152 * This is a separate function to make it obvious from the crash
153 * reports that this crash is deliberate and for testing purposes.
154 */
NaClSignalTestCrashOnStartup(void)155 void NaClSignalTestCrashOnStartup(void) {
156 if (getenv("NACL_CRASH_TEST") != NULL) {
157 NaClSignalErrorMessage("[CRASH_TEST] Causing crash in NaCl "
158 "trusted code...\n");
159 /*
160 * Clang transmutes a NULL pointer reference into a generic
161 * "undefined" case. That code crashes with a different signal
162 * than an actual bad pointer reference, violating the tests'
163 * expectations. A pointer that is known bad but is not literally
164 * NULL does not get this treatment.
165 */
166 *(volatile int *) 1 = 0;
167 }
168 }
169
NaClUserRegisterStateFromSignalContext(volatile NaClUserRegisterState * dest,const struct NaClSignalContext * src)170 static void NaClUserRegisterStateFromSignalContext(
171 volatile NaClUserRegisterState *dest,
172 const struct NaClSignalContext *src) {
173 #define COPY_REG(reg) dest->reg = src->reg
174 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
175 COPY_REG(eax);
176 COPY_REG(ecx);
177 COPY_REG(edx);
178 COPY_REG(ebx);
179 COPY_REG(stack_ptr);
180 COPY_REG(ebp);
181 COPY_REG(esi);
182 COPY_REG(edi);
183 COPY_REG(prog_ctr);
184 COPY_REG(flags);
185 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
186 COPY_REG(rax);
187 COPY_REG(rcx);
188 COPY_REG(rdx);
189 COPY_REG(rbx);
190 COPY_REG(stack_ptr);
191 COPY_REG(rbp);
192 COPY_REG(rsi);
193 COPY_REG(rdi);
194 COPY_REG(r8);
195 COPY_REG(r9);
196 COPY_REG(r10);
197 COPY_REG(r11);
198 COPY_REG(r12);
199 COPY_REG(r13);
200 COPY_REG(r14);
201 COPY_REG(r15);
202 COPY_REG(prog_ctr);
203 COPY_REG(flags);
204 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
205 COPY_REG(r0);
206 COPY_REG(r1);
207 COPY_REG(r2);
208 COPY_REG(r3);
209 COPY_REG(r4);
210 COPY_REG(r5);
211 COPY_REG(r6);
212 COPY_REG(r7);
213 COPY_REG(r8);
214 /* Don't leak the address of NaClAppThread by reporting r9's value here. */
215 dest->r9 = -1;
216 COPY_REG(r10);
217 COPY_REG(r11);
218 COPY_REG(r12);
219 COPY_REG(stack_ptr);
220 COPY_REG(lr);
221 COPY_REG(prog_ctr);
222 COPY_REG(cpsr);
223 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
224 COPY_REG(zero);
225 COPY_REG(at);
226 COPY_REG(v0);
227 COPY_REG(v1);
228 COPY_REG(a0);
229 COPY_REG(a1);
230 COPY_REG(a2);
231 COPY_REG(a3);
232 COPY_REG(t0);
233 COPY_REG(t1);
234 COPY_REG(t2);
235 COPY_REG(t3);
236 COPY_REG(t4);
237 COPY_REG(t5);
238 COPY_REG(t6);
239 COPY_REG(t7);
240 COPY_REG(s0);
241 COPY_REG(s1);
242 COPY_REG(s2);
243 COPY_REG(s3);
244 COPY_REG(s4);
245 COPY_REG(s5);
246 COPY_REG(s6);
247 COPY_REG(s7);
248 COPY_REG(t8);
249 COPY_REG(t9);
250 COPY_REG(k0);
251 COPY_REG(k1);
252 COPY_REG(global_ptr);
253 COPY_REG(stack_ptr);
254 COPY_REG(frame_ptr);
255 COPY_REG(return_addr);
256 COPY_REG(prog_ctr);
257 #else
258 # error Unsupported architecture
259 #endif
260 #undef COPY_REG
261 }
262
263 /*
264 * The |frame| argument is volatile because this function writes
265 * directly into untrusted address space on Linux.
266 */
NaClSignalSetUpExceptionFrame(volatile struct NaClExceptionFrame * frame,const struct NaClSignalContext * regs,uint32_t context_user_addr)267 void NaClSignalSetUpExceptionFrame(volatile struct NaClExceptionFrame *frame,
268 const struct NaClSignalContext *regs,
269 uint32_t context_user_addr) {
270 unsigned i;
271
272 /*
273 * Use the end of frame->portable for the size, avoiding padding
274 * added after it within NaClExceptionFrame.
275 */
276 frame->context.size =
277 (uint32_t) ((uintptr_t) (&frame->portable + 1) -
278 (uintptr_t) &frame->context);
279 frame->context.portable_context_offset =
280 (uint32_t) ((uintptr_t) &frame->portable -
281 (uintptr_t) &frame->context);
282 frame->context.portable_context_size = sizeof(frame->portable);
283 frame->context.arch = NACL_ELF_E_MACHINE;
284 frame->context.regs_size = sizeof(frame->context.regs);
285
286 /* Avoid memset() here because |frame| is volatile. */
287 for (i = 0; i < NACL_ARRAY_SIZE(frame->context.reserved); i++) {
288 frame->context.reserved[i] = 0;
289 }
290
291 NaClUserRegisterStateFromSignalContext(&frame->context.regs, regs);
292
293 frame->portable.prog_ctr = (uint32_t) regs->prog_ctr;
294 frame->portable.stack_ptr = (uint32_t) regs->stack_ptr;
295
296 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
297 frame->context_ptr = context_user_addr;
298 frame->portable.frame_ptr = (uint32_t) regs->ebp;
299 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
300 UNREFERENCED_PARAMETER(context_user_addr);
301 frame->portable.frame_ptr = (uint32_t) regs->rbp;
302 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
303 UNREFERENCED_PARAMETER(context_user_addr);
304 frame->portable.frame_ptr = regs->r11;
305 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
306 UNREFERENCED_PARAMETER(context_user_addr);
307 frame->portable.frame_ptr = regs->frame_ptr;
308 #else
309 # error Unsupported architecture
310 #endif
311
312 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
313 /*
314 * Returning from the exception handler is not possible, so to avoid
315 * any confusion that might arise from jumping to an uninitialised
316 * address, we set the return address to zero.
317 */
318 frame->return_addr = 0;
319 #endif
320 }
321