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.
13 #include "native_client/src/include/nacl/nacl_exception.h"
14 #include "native_client/src/trusted/service_runtime/include/sys/nacl_test_crash.h"
15 #include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h"
18 char stack_in_rwdata[0x1000];
21 * Note that we have to provide an initialiser here otherwise gcc
22 * defines this using ".comm" and the variable does not get put into a
25 const char stack_in_rodata[0x1000] = "blah";
28 void test_bad_handler(void) {
30 * Use an address that we know contains no valid code, yet is within
31 * the code segment range and is well-aligned. The bottom 64k of
32 * address space is never mapped.
34 nacl_exception_handler_t handler = (nacl_exception_handler_t) 0x1000;
35 int rc = nacl_exception_set_handler(handler);
37 fprintf(stderr, "** intended_exit_status=untrusted_segfault\n");
39 *(volatile int *) 0 = 0;
43 #if defined(__i386__) || defined(__x86_64__)
46 * If there are TCB bugs, it is possible that the untrusted exception
47 * handler gets run on a bad stack, either because the TCB ignored the
48 * error it got when writing the exception frame, or because page
49 * protections were ignored when writing the exception frame. (The
50 * latter is a problem with WriteProcessMemory() on Windows: see
51 * http://code.google.com/p/nativeclient/issues/detail?id=2536.)
53 * If that happens, the test needs to report the problem. In order to
54 * do that, our exception handler needs to restore a working stack.
57 char recovery_stack[0x1000] __attribute__((aligned(16)));
59 void bad_stack_exception_handler(struct NaClExceptionContext *context);
60 __asm__(".pushsection .text, \"ax\", @progbits\n"
62 "bad_stack_exception_handler:\n"
63 /* Restore a working stack, allowing for alignment. */
64 # if defined(__i386__)
65 "mov $recovery_stack - 4, %esp\n"
67 # elif defined(__x86_64__)
68 "naclrestsp $recovery_stack - 8, %r15\n"
73 void error_exit(void) {
77 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
79 char recovery_stack[0x1000] __attribute__((aligned(8)));
81 void bad_stack_exception_handler(struct NaClExceptionContext *context);
82 __asm__(".pushsection .text, \"ax\", @progbits\n"
84 ".global bad_stack_exception_handler\n"
85 "bad_stack_exception_handler:\n"
86 "lui $t9, %hi(recovery_stack)\n"
87 "addiu $sp, $t9, %lo(recovery_stack)\n"
89 "lui $t9, %hi(error_exit)\n"
91 "addiu $t9, $t9, %lo(error_exit)\n"
103 /* TODO(mseaborn): Implement a stack switcher, like the one above, for ARM. */
104 void bad_stack_exception_handler(struct NaClExceptionContext *context) {
112 * This checks that the process terminates safely if the system
113 * attempts to write a stack frame at the current stack pointer when
114 * the stack pointer points outside of the sandbox's address space.
116 * This only applies to x86-32, because the stack pointer register
117 * cannot be set to point outside of the sandbox's address space on
120 void test_stack_outside_sandbox(void) {
121 #if defined(__i386__)
122 int rc = nacl_exception_set_handler(bad_stack_exception_handler);
124 fprintf(stderr, "** intended_exit_status=untrusted_segfault\n");
127 * Set the stack pointer to an address that is definitely
128 * outside the sandbox's address space.
130 "movl $0xffffffff, %esp\n"
134 fprintf(stderr, "test_bad_stack does not apply on this platform\n");
135 fprintf(stderr, "** intended_exit_status=0\n");
142 * This test case does not crash. It successfully runs
143 * bad_stack_exception_handler() in order to check that it works, so
144 * that we can be sure that other tests do not crash (and hence pass)
147 void test_stack_in_rwdata(void) {
148 int rc = nacl_exception_set_handler(bad_stack_exception_handler);
150 rc = nacl_exception_set_stack((void *) stack_in_rwdata,
151 sizeof(stack_in_rwdata));
153 fprintf(stderr, "** intended_exit_status=1\n");
155 *(volatile int *) 0 = 0;
160 * This reproduced a problem with NaCl's exception handler on Mac OS
161 * X, in which sel_ldr would hang when attempting to write to an
164 void test_stack_in_rodata(void) {
165 int rc = nacl_exception_set_handler(bad_stack_exception_handler);
167 rc = nacl_exception_set_stack((void *) stack_in_rodata,
168 sizeof(stack_in_rodata));
170 fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n");
172 *(volatile int *) 0 = 0;
177 * The test below reproduced a problem with NaCl's exception handler
178 * on Windows where WriteProcessMemory() would bypass page permissions
179 * and write the exception frame into the code area.
181 #if defined(__i386__) || defined(__x86_64__)
183 * If we have an assembler, we can reserve some of the code area so
184 * that we do not risk overwriting bad_stack_exception_handler() if
187 * This might be in the static or dynamic code area depending on
188 * whether this executable is statically or dynamically linked, and
189 * the two areas potentially behave differently. Therefore, for full
190 * coverage, this test should be run as both statically and
191 * dynamically linked.
193 __asm__(".pushsection .text, \"ax\", @progbits\n"
195 ".fill 0x1000, 1, 0x90\n" /* Fill with NOPs (90) */
197 extern char stack_in_code[];
199 /* Otherwise just use the start of the static code area. */
200 char *stack_in_code = (char *) 0x20000;
202 const int stack_in_code_size = 0x1000;
204 void test_stack_in_code(void) {
205 int rc = nacl_exception_set_handler(bad_stack_exception_handler);
207 rc = nacl_exception_set_stack(stack_in_code, stack_in_code_size);
209 fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n");
211 *(volatile int *) 0 = 0;
216 * This checks that crashes in trusted code (such as inside NaCl
217 * syscalls) do not cause the untrusted exception handler to run.
219 void test_crash_in_syscall(void) {
220 int rc = nacl_exception_set_handler(bad_stack_exception_handler);
222 rc = nacl_exception_set_stack((void *) stack_in_rwdata,
223 sizeof(stack_in_rwdata));
225 fprintf(stderr, "** intended_exit_status=trusted_segfault\n");
227 * Cause a crash inside a NaCl syscall.
229 NACL_SYSCALL(test_crash)(NACL_TEST_CRASH_MEMORY);
230 /* Should not reach here. */
235 int main(int argc, char **argv) {
237 fprintf(stderr, "Usage: program <test-name>\n");
241 #define TRY_TEST(test_name) \
242 if (strcmp(argv[1], #test_name) == 0) { test_name(); return 1; }
244 TRY_TEST(test_bad_handler);
245 TRY_TEST(test_stack_outside_sandbox);
246 TRY_TEST(test_stack_in_rwdata);
247 TRY_TEST(test_stack_in_rodata);
248 TRY_TEST(test_stack_in_code);
249 TRY_TEST(test_crash_in_syscall);
251 fprintf(stderr, "Error: Unknown test: \"%s\"\n", argv[1]);