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