Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client / tests / exception_test / exception_crash_test.c
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 <assert.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12
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"
16
17
18 char stack_in_rwdata[0x1000];
19
20 /*
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
23  * read-only segment.
24  */
25 const char stack_in_rodata[0x1000] = "blah";
26
27
28 void test_bad_handler(void) {
29   /*
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.
33    */
34   nacl_exception_handler_t handler = (nacl_exception_handler_t) 0x1000;
35   int rc = nacl_exception_set_handler(handler);
36   assert(rc == 0);
37   fprintf(stderr, "** intended_exit_status=untrusted_segfault\n");
38   /* Cause crash. */
39   *(volatile int *) 0 = 0;
40 }
41
42
43 #if defined(__i386__) || defined(__x86_64__)
44
45 /*
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.)
52  *
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.
55  */
56
57 char recovery_stack[0x1000] __attribute__((aligned(16)));
58
59 void bad_stack_exception_handler(struct NaClExceptionContext *context);
60 __asm__(".pushsection .text, \"ax\", @progbits\n"
61         ".p2align 5\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"
66         "jmp error_exit\n"
67 # elif defined(__x86_64__)
68         "naclrestsp $recovery_stack - 8, %r15\n"
69         "jmp error_exit\n"
70 # endif
71         ".popsection\n");
72
73 void error_exit(void) {
74   _exit(1);
75 }
76
77 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
78
79 char recovery_stack[0x1000] __attribute__((aligned(8)));
80
81 void bad_stack_exception_handler(struct NaClExceptionContext *context);
82 __asm__(".pushsection .text, \"ax\", @progbits\n"
83         ".set noreorder\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"
88         "and   $sp, $sp, $t7\n"
89         "lui   $t9,      %hi(error_exit)\n"
90         "j error_exit\n"
91         "addiu $t9, $t9, %lo(error_exit)\n"
92         "nop\n"
93         "nop\n"
94         ".set reorder\n"
95         ".popsection\n");
96
97 void error_exit() {
98   _exit(1);
99 }
100
101 #else
102
103 /* TODO(mseaborn): Implement a stack switcher, like the one above, for ARM. */
104 void bad_stack_exception_handler(struct NaClExceptionContext *context) {
105   _exit(1);
106 }
107
108 #endif
109
110
111 /*
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.
115  *
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
118  * x86-64 and ARM.
119  */
120 void test_stack_outside_sandbox(void) {
121 #if defined(__i386__)
122   int rc = nacl_exception_set_handler(bad_stack_exception_handler);
123   assert(rc == 0);
124   fprintf(stderr, "** intended_exit_status=untrusted_segfault\n");
125   __asm__(
126       /*
127        * Set the stack pointer to an address that is definitely
128        * outside the sandbox's address space.
129        */
130       "movl $0xffffffff, %esp\n"
131       /* Cause crash. */
132       "movl $0, 0\n");
133 #else
134   fprintf(stderr, "test_bad_stack does not apply on this platform\n");
135   fprintf(stderr, "** intended_exit_status=0\n");
136   exit(0);
137 #endif
138 }
139
140
141 /*
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)
145  * accidentally.
146  */
147 void test_stack_in_rwdata(void) {
148   int rc = nacl_exception_set_handler(bad_stack_exception_handler);
149   assert(rc == 0);
150   rc = nacl_exception_set_stack((void *) stack_in_rwdata,
151                                 sizeof(stack_in_rwdata));
152   assert(rc == 0);
153   fprintf(stderr, "** intended_exit_status=1\n");
154   /* Cause crash. */
155   *(volatile int *) 0 = 0;
156 }
157
158
159 /*
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
162  * unwritable stack.
163  */
164 void test_stack_in_rodata(void) {
165   int rc = nacl_exception_set_handler(bad_stack_exception_handler);
166   assert(rc == 0);
167   rc = nacl_exception_set_stack((void *) stack_in_rodata,
168                                 sizeof(stack_in_rodata));
169   assert(rc == 0);
170   fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n");
171   /* Cause crash. */
172   *(volatile int *) 0 = 0;
173 }
174
175
176 /*
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.
180  */
181 #if defined(__i386__) || defined(__x86_64__)
182 /*
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
185  * the test fails.
186  *
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.
192  */
193 __asm__(".pushsection .text, \"ax\", @progbits\n"
194         "stack_in_code:\n"
195         ".fill 0x1000, 1, 0x90\n" /* Fill with NOPs (90) */
196         ".popsection\n");
197 extern char stack_in_code[];
198 #else
199 /* Otherwise just use the start of the static code area. */
200 char *stack_in_code = (char *) 0x20000;
201 #endif
202 const int stack_in_code_size = 0x1000;
203
204 void test_stack_in_code(void) {
205   int rc = nacl_exception_set_handler(bad_stack_exception_handler);
206   assert(rc == 0);
207   rc = nacl_exception_set_stack(stack_in_code, stack_in_code_size);
208   assert(rc == 0);
209   fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n");
210   /* Cause crash. */
211   *(volatile int *) 0 = 0;
212 }
213
214
215 /*
216  * This checks that crashes in trusted code (such as inside NaCl
217  * syscalls) do not cause the untrusted exception handler to run.
218  */
219 void test_crash_in_syscall(void) {
220   int rc = nacl_exception_set_handler(bad_stack_exception_handler);
221   assert(rc == 0);
222   rc = nacl_exception_set_stack((void *) stack_in_rwdata,
223                                 sizeof(stack_in_rwdata));
224   assert(rc == 0);
225   fprintf(stderr, "** intended_exit_status=trusted_segfault\n");
226   /*
227    * Cause a crash inside a NaCl syscall.
228    */
229   NACL_SYSCALL(test_crash)(NACL_TEST_CRASH_MEMORY);
230   /* Should not reach here. */
231   _exit(1);
232 }
233
234
235 int main(int argc, char **argv) {
236   if (argc != 2) {
237     fprintf(stderr, "Usage: program <test-name>\n");
238     return 1;
239   }
240
241 #define TRY_TEST(test_name) \
242     if (strcmp(argv[1], #test_name) == 0) { test_name(); return 1; }
243
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);
250
251   fprintf(stderr, "Error: Unknown test: \"%s\"\n", argv[1]);
252   return 1;
253 }