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.
16 #include "native_client/src/include/elf_constants.h"
17 #include "native_client/src/include/nacl/nacl_exception.h"
18 #include "native_client/src/trusted/service_runtime/include/sys/nacl_syscalls.h"
19 #include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h"
20 #include "native_client/tests/common/register_set.h"
21 #include "native_client/tests/inbrowser_test_runner/test_runner.h"
25 * This is used for calculating the size of the exception stack frame
26 * when alignment is taken into account.
28 struct CombinedContext {
29 struct NaClExceptionContext c1;
30 struct NaClExceptionPortableContext c2;
35 struct NaClSignalContext g_regs_at_crash;
38 char *g_registered_stack;
39 size_t g_registered_stack_size;
43 #define STACK_ALIGNMENT 8
45 #define STACK_ALIGNMENT 16
49 const int kReturnAddrSize = 4;
50 const int kArgSizeOnStack = 4;
51 const int kRedZoneSize = 0;
52 #elif defined(__x86_64__)
53 const int kReturnAddrSize = 8;
54 const int kArgSizeOnStack = 0;
55 const int kRedZoneSize = 128;
56 #elif defined(__arm__)
57 const int kReturnAddrSize = 0;
58 const int kArgSizeOnStack = 0;
59 const int kRedZoneSize = 0;
60 #elif defined(__mips__)
61 const int kReturnAddrSize = 0;
62 const int kArgSizeOnStack = 16;
63 const int kRedZoneSize = 0;
65 # error Unsupported architecture
68 void crash_at_known_address(void);
69 extern char prog_ctr_at_crash[];
71 __asm__(".pushsection .text, \"ax\", @progbits\n"
73 "crash_at_known_address:\n"
74 "prog_ctr_at_crash:\n"
77 #elif defined(__x86_64__)
78 __asm__(".pushsection .text, \"ax\", @progbits\n"
80 "crash_at_known_address:\n"
81 "prog_ctr_at_crash:\n"
84 #elif defined(__arm__)
85 __asm__(".pushsection .text, \"ax\", %progbits\n"
87 "crash_at_known_address:\n"
89 "bic r0, r0, #0xc0000000\n"
90 "prog_ctr_at_crash:\n"
93 #elif defined(__mips__)
94 __asm__(".pushsection .text, \"ax\", @progbits\n"
96 ".global crash_at_known_address\n"
97 "crash_at_known_address:\n"
98 "and $zero, $zero, $t7\n"
99 ".global prog_ctr_at_crash\n"
100 "prog_ctr_at_crash:\n"
104 # error Unsupported architecture
108 void return_from_exception_handler(void) {
110 * Clear the exception flag so that future faults will invoke the
113 int rc = nacl_exception_clear_flag();
115 longjmp(g_jmp_buf, 1);
118 void simple_exception_handler(struct NaClExceptionContext *regs) {
119 return_from_exception_handler();
122 void exception_handler(struct NaClExceptionContext *context);
123 REGS_SAVER_FUNC_NOPROTO(exception_handler, exception_handler_wrapped);
125 void exception_handler_wrapped(struct NaClSignalContext *entry_regs) {
126 struct NaClExceptionContext *context =
127 (struct NaClExceptionContext *) RegsGetArg1(entry_regs);
128 struct NaClExceptionPortableContext *portable =
129 nacl_exception_context_get_portable(context);
131 printf("handler called\n");
133 assert(context->size == (uintptr_t) (portable + 1) - (uintptr_t) context);
134 assert(context->portable_context_size ==
135 sizeof(struct NaClExceptionPortableContext));
136 assert(context->regs_size == sizeof(NaClUserRegisterState));
138 assert(portable->stack_ptr == (uint32_t) g_regs_at_crash.stack_ptr);
139 assert(portable->prog_ctr == (uintptr_t) prog_ctr_at_crash);
140 #if defined(__i386__)
141 assert(portable->frame_ptr == g_regs_at_crash.ebp);
142 assert(context->arch == EM_386);
143 #elif defined(__x86_64__)
144 assert(portable->frame_ptr == (uint32_t) g_regs_at_crash.rbp);
145 assert(context->arch == EM_X86_64);
146 #elif defined(__arm__)
147 assert(portable->frame_ptr == g_regs_at_crash.r11);
148 assert(context->arch == EM_ARM);
149 assert(context->regs.r9 == -1);
150 #elif defined(__mips__)
151 assert(portable->frame_ptr == g_regs_at_crash.frame_ptr);
152 assert(context->arch == EM_MIPS);
154 # error Unsupported architecture
158 * Convert the NaClUserRegisterState to a NaClSignalContext so that
159 * we can reuse RegsAssertEqual() to compare the register state.
161 struct NaClSignalContext reported_regs;
162 RegsCopyFromUserRegisterState(&reported_regs, &context->regs);
163 RegsAssertEqual(&reported_regs, &g_regs_at_crash);
165 const int kMaxStackFrameSize = 0x1000;
168 if (g_registered_stack == NULL) {
169 /* Check that our current stack is just below the saved stack pointer. */
170 stack_top = (char *) portable->stack_ptr - kRedZoneSize;
171 assert(stack_top - kMaxStackFrameSize < &local_var);
172 assert(&local_var < stack_top);
174 /* Check that we are running on the exception stack. */
175 stack_top = g_registered_stack + g_registered_stack_size;
176 assert(g_registered_stack <= &local_var);
177 assert(&local_var < stack_top);
180 /* Check the exception handler's initial stack pointer more exactly. */
181 uintptr_t frame_base = entry_regs->stack_ptr + kReturnAddrSize;
182 assert(frame_base % STACK_ALIGNMENT == 0);
183 char *frame_top = (char *) (frame_base + kArgSizeOnStack +
184 sizeof(struct CombinedContext));
185 /* Check that no more than the stack alignment size is wasted. */
186 assert(stack_top - STACK_ALIGNMENT < frame_top);
187 assert(frame_top <= stack_top);
189 #if defined(__x86_64__)
190 /* Check that %rsp and %rbp have safe, %r15-extended values. */
191 assert(entry_regs->stack_ptr >> 32 == entry_regs->r15 >> 32);
192 assert(entry_regs->rbp >> 32 == entry_regs->r15 >> 32);
195 return_from_exception_handler();
198 void test_exception_stack_with_size(char *stack, size_t stack_size) {
199 if (0 != nacl_exception_set_handler(exception_handler)) {
200 printf("failed to set exception handler\n");
203 if (0 != nacl_exception_set_stack(stack, stack_size)) {
204 printf("failed to set alt stack\n");
207 g_registered_stack = stack;
208 g_registered_stack_size = stack_size;
210 char crash_stack[0x1000];
211 RegsFillTestValues(&g_regs_at_crash, /* seed= */ 0);
212 g_regs_at_crash.stack_ptr = (uintptr_t) crash_stack + sizeof(crash_stack);
213 g_regs_at_crash.prog_ctr = (uintptr_t) prog_ctr_at_crash;
214 RegsApplySandboxConstraints(&g_regs_at_crash);
216 /* crash_at_known_address clobbers r0. */
217 g_regs_at_crash.r0 = 0;
220 if (!setjmp(g_jmp_buf)) {
221 JUMP_WITH_REGS(&g_regs_at_crash, crash_at_known_address);
223 /* Clear the jmp_buf to prevent it from being reused accidentally. */
224 memset(g_jmp_buf, 0, sizeof(g_jmp_buf));
227 void test_exceptions_minimally(void) {
228 /* Test exceptions without having an exception stack set up. */
229 test_exception_stack_with_size(NULL, 0);
231 test_exception_stack_with_size(stack, sizeof(stack));
234 void test_exception_stack_alignments(void) {
236 * Test the stack realignment logic by trying stacks which end at
237 * different addresses modulo the stack alignment size.
240 for (diff = 0; diff <= STACK_ALIGNMENT * 2; diff++) {
241 test_exception_stack_with_size(stack, sizeof(stack) - diff);
245 void test_getting_previous_handler(void) {
247 * The direct IRT call and NaCl syscall exposes old handler as API
248 * whereas nacl_exception_set_handler() does not. This test
249 * exercises that path.
252 nacl_exception_handler_t prev_handler;
254 rc = NACL_SYSCALL(exception_handler)(exception_handler, NULL);
257 rc = NACL_SYSCALL(exception_handler)(NULL, &prev_handler);
259 assert(prev_handler == exception_handler);
261 rc = NACL_SYSCALL(exception_handler)(NULL, &prev_handler);
263 assert(prev_handler == NULL);
266 void test_invalid_handlers(void) {
268 nacl_exception_handler_t unaligned_func_ptr =
269 (nacl_exception_handler_t) ((uintptr_t) exception_handler + 1);
270 const char *ptr_in_rodata_segment = "";
272 /* An alignment check is required for safety in all NaCl sandboxes. */
273 rc = nacl_exception_set_handler(unaligned_func_ptr);
274 assert(rc == EFAULT);
276 /* A range check is required for safety in the NaCl ARM sandbox. */
277 rc = nacl_exception_set_handler(
278 (nacl_exception_handler_t) (uintptr_t) ptr_in_rodata_segment);
279 assert(rc == EFAULT);
283 void *thread_func(void *unused_arg) {
285 * This tests that the exception handler gets the correct
286 * NaClAppThread for this thread. If it gets the wrong thread, the
287 * handler will detect that the stack it is running on does not
288 * match the stack that was registered.
290 * On x86-64 Windows, it is non-trivial for an out-of-process
291 * exception handler to determine the NaClAppThread for a faulting
292 * thread, which is why we need a test for this.
294 test_exception_stack_with_size(NULL, 0);
295 test_exception_stack_with_size(stack, sizeof(stack));
299 void test_exceptions_on_non_main_thread(void) {
301 int rc = pthread_create(&tid, NULL, thread_func, NULL);
303 rc = pthread_join(tid, NULL);
307 void test_catching_various_exception_types(void) {
308 int rc = nacl_exception_set_handler(simple_exception_handler);
311 printf("Testing __builtin_trap()...\n");
312 if (!setjmp(g_jmp_buf)) {
317 #if defined(__i386__) || defined(__x86_64__)
318 printf("Testing hlt...\n");
319 if (!setjmp(g_jmp_buf)) {
323 printf("Testing ud2a (an illegal instruction)...\n");
324 if (!setjmp(g_jmp_buf)) {
328 printf("Testing integer division by zero...\n");
329 if (!setjmp(g_jmp_buf)) {
331 __asm__ volatile("idivb %1"
333 : "r"((uint8_t) 0), "a"((uint16_t) 1));
338 /* Clear the jmp_buf to prevent it from being reused accidentally. */
339 memset(g_jmp_buf, 0, sizeof(g_jmp_buf));
343 #if defined(__i386__) || defined(__x86_64__)
345 int get_x86_direction_flag(void);
347 void test_get_x86_direction_flag(void) {
349 * Sanity check: Ensure that get_x86_direction_flag() works. We
350 * avoid calling assert() with the flag set, because that might not
353 assert(get_x86_direction_flag() == 0);
355 int flag = get_x86_direction_flag();
360 void direction_flag_exception_handler(struct NaClExceptionContext *context) {
361 assert(get_x86_direction_flag() == 0);
362 return_from_exception_handler();
366 * The x86 ABIs require that the x86 direction flag is unset on entry
367 * to a function. However, a crash could occur while the direction
368 * flag is set. In order for an exception handler to be a normal
369 * function without x86-specific knowledge, NaCl needs to unset the
370 * direction flag when calling the exception handler. This test
371 * checks that this happens.
373 void test_unsetting_x86_direction_flag(void) {
374 int rc = nacl_exception_set_handler(direction_flag_exception_handler);
376 if (!setjmp(g_jmp_buf)) {
377 /* Cause a crash with the direction flag set. */
379 *((volatile int *) 0) = 0;
380 /* Should not reach here. */
383 /* Clear the jmp_buf to prevent it from being reused accidentally. */
384 memset(g_jmp_buf, 0, sizeof(g_jmp_buf));
390 void run_test(const char *test_name, void (*test_func)(void)) {
391 printf("Running %s...\n", test_name);
395 #define RUN_TEST(test_func) (run_test(#test_func, test_func))
398 RUN_TEST(test_exceptions_minimally);
399 RUN_TEST(test_exception_stack_alignments);
400 RUN_TEST(test_getting_previous_handler);
401 RUN_TEST(test_invalid_handlers);
402 /* pthread_join() is broken under qemu-arm. */
403 if (getenv("UNDER_QEMU_ARM") == NULL)
404 RUN_TEST(test_exceptions_on_non_main_thread);
405 RUN_TEST(test_catching_various_exception_types);
407 #if defined(__i386__) || defined(__x86_64__)
408 RUN_TEST(test_get_x86_direction_flag);
409 RUN_TEST(test_unsetting_x86_direction_flag);
412 fprintf(stderr, "** intended_exit_status=0\n");
417 return RunTests(TestMain);