OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <assert.h> | |
6 #include <dirent.h> | |
7 #include <pthread.h> | |
8 #include <pty.h> | |
9 #include <sys/types.h> | |
10 #include <sys/wait.h> | |
11 | |
12 #include "sandbox_impl.h" | |
13 | |
14 #ifdef DEBUG | |
15 #define MSG(fmt, ...) printf(fmt, ##__VA_ARGS__) | |
16 #else | |
17 #define MSG(fmt, ...) do { } while (0) | |
18 #endif | |
19 | |
20 int g_intended_status_fd = -1; | |
21 | |
22 // Declares the wait() status that the test subprocess intends to exit with. | |
23 void intend_exit_status(int val, bool is_signal) { | |
24 if (is_signal) { | |
25 val = W_EXITCODE(0, val); | |
26 } else { | |
27 val = W_EXITCODE(val, 0); | |
28 } | |
29 if (g_intended_status_fd != -1) { | |
30 int sent = write(g_intended_status_fd, &val, sizeof(val)); | |
31 assert(sent == sizeof(val)); | |
32 } else { | |
33 // This prints in cases where we run one test without forking | |
34 printf("Intending to exit with status %i...\n", val); | |
35 } | |
36 } | |
37 | |
38 | |
39 // This is basically a marker to grep for. | |
40 #define TEST(name) void name() | |
41 | |
42 TEST(test_dup) { | |
43 StartSeccompSandbox(); | |
44 // Test a simple syscall that is marked as UNRESTRICTED_SYSCALL. | |
45 int fd = dup(1); | |
46 assert(fd >= 0); | |
47 int rc = close(fd); | |
48 assert(rc == 0); | |
49 } | |
50 | |
51 TEST(test_segfault) { | |
52 StartSeccompSandbox(); | |
53 // Check that the sandbox's SIGSEGV handler does not stop the | |
54 // process from dying cleanly in the event of a real segfault. | |
55 intend_exit_status(SIGSEGV, true); | |
56 asm("hlt"); | |
57 } | |
58 | |
59 TEST(test_exit) { | |
60 StartSeccompSandbox(); | |
61 intend_exit_status(123, false); | |
62 _exit(123); | |
63 } | |
64 | |
65 // This has an off-by-three error because it counts ".", "..", and the | |
66 // FD for the /proc/self/fd directory. This doesn't matter because it | |
67 // is only used to check for differences in the number of open FDs. | |
68 static int count_fds() { | |
69 DIR *dir = opendir("/proc/self/fd"); | |
70 assert(dir != NULL); | |
71 int count = 0; | |
72 while (1) { | |
73 struct dirent *d = readdir(dir); | |
74 if (d == NULL) | |
75 break; | |
76 count++; | |
77 } | |
78 int rc = closedir(dir); | |
79 assert(rc == 0); | |
80 return count; | |
81 } | |
82 | |
83 static void *thread_func(void *x) { | |
84 int *ptr = (int *) x; | |
85 *ptr = 123; | |
86 MSG("In new thread\n"); | |
87 return (void *) 456; | |
88 } | |
89 | |
90 TEST(test_thread) { | |
91 playground::g_policy.allow_file_namespace = true; // To allow count_fds() | |
92 StartSeccompSandbox(); | |
93 int fd_count1 = count_fds(); | |
94 pthread_t tid; | |
95 int x = 999; | |
96 void *result; | |
97 pthread_create(&tid, NULL, thread_func, &x); | |
98 MSG("Waiting for thread\n"); | |
99 pthread_join(tid, &result); | |
100 assert(result == (void *) 456); | |
101 assert(x == 123); | |
102 // Check that the process has not leaked FDs. | |
103 int fd_count2 = count_fds(); | |
104 assert(fd_count2 == fd_count1); | |
105 } | |
106 | |
107 static int clone_func(void *x) { | |
108 int *ptr = (int *) x; | |
109 *ptr = 124; | |
110 MSG("In thread\n"); | |
111 // On x86-64, returning from this function calls the __NR_exit_group | |
112 // syscall instead of __NR_exit. | |
113 syscall(__NR_exit, 100); | |
114 // Not reached. | |
115 return 200; | |
116 } | |
117 | |
118 #if defined(__i386__) | |
119 static int get_gs() { | |
120 int gs; | |
121 asm volatile("mov %%gs, %0" : "=r"(gs)); | |
122 return gs; | |
123 } | |
124 #endif | |
125 | |
126 static void *get_tls_base() { | |
127 void *base; | |
128 #if defined(__x86_64__) | |
129 asm volatile("mov %%fs:0, %0" : "=r"(base)); | |
130 #elif defined(__i386__) | |
131 asm volatile("mov %%gs:0, %0" : "=r"(base)); | |
132 #else | |
133 #error Unsupported target platform | |
134 #endif | |
135 return base; | |
136 } | |
137 | |
138 TEST(test_clone) { | |
139 playground::g_policy.allow_file_namespace = true; // To allow count_fds() | |
140 StartSeccompSandbox(); | |
141 int fd_count1 = count_fds(); | |
142 int stack_size = 0x1000; | |
143 char *stack = (char *) malloc(stack_size); | |
144 assert(stack != NULL); | |
145 int flags = CLONE_VM | CLONE_FS | CLONE_FILES | | |
146 CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | | |
147 CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; | |
148 int tid = -1; | |
149 int x = 999; | |
150 | |
151 // The sandbox requires us to pass CLONE_TLS. Pass settings that | |
152 // are enough to copy the parent thread's TLS setup. This allows us | |
153 // to invoke libc in the child thread. | |
154 #if defined(__x86_64__) | |
155 void *tls = get_tls_base(); | |
156 #elif defined(__i386__) | |
157 struct user_desc tls_desc, *tls = &tls_desc; | |
158 tls_desc.entry_number = get_gs() >> 3; | |
159 tls_desc.base_addr = (long) get_tls_base(); | |
160 tls_desc.limit = 0xfffff; | |
161 tls_desc.seg_32bit = 1; | |
162 tls_desc.contents = 0; | |
163 tls_desc.read_exec_only = 0; | |
164 tls_desc.limit_in_pages = 1; | |
165 tls_desc.seg_not_present = 0; | |
166 tls_desc.useable = 1; | |
167 #else | |
168 #error Unsupported target platform | |
169 #endif | |
170 | |
171 int rc = clone(clone_func, (void *) (stack + stack_size), flags, &x, | |
172 &tid, tls, &tid); | |
173 assert(rc > 0); | |
174 while (tid == rc) { | |
175 syscall(__NR_futex, &tid, FUTEX_WAIT, rc, NULL); | |
176 } | |
177 assert(tid == 0); | |
178 assert(x == 124); | |
179 // Check that the process has not leaked FDs. | |
180 int fd_count2 = count_fds(); | |
181 assert(fd_count2 == fd_count1); | |
182 } | |
183 | |
184 static int uncalled_clone_func(void *x) { | |
185 printf("In thread func, which shouldn't happen\n"); | |
186 return 1; | |
187 } | |
188 | |
189 TEST(test_clone_disallowed_flags) { | |
190 StartSeccompSandbox(); | |
191 int stack_size = 4096; | |
192 char *stack = (char *) malloc(stack_size); | |
193 assert(stack != NULL); | |
194 /* We omit the flags CLONE_SETTLS, CLONE_PARENT_SETTID and | |
195 CLONE_CHILD_CLEARTID, which is disallowed by the sandbox. */ | |
196 int flags = CLONE_VM | CLONE_FS | CLONE_FILES | | |
197 CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; | |
198 int rc = clone(uncalled_clone_func, (void *) (stack + stack_size), | |
199 flags, NULL, NULL, NULL, NULL); | |
200 assert(rc == -1); | |
201 assert(errno == EPERM); | |
202 } | |
203 | |
204 static void *fp_thread(void *x) { | |
205 int val; | |
206 asm("movss %%xmm0, %0" : "=m"(val)); | |
207 MSG("val=%i\n", val); | |
208 return NULL; | |
209 } | |
210 | |
211 TEST(test_fp_regs) { | |
212 StartSeccompSandbox(); | |
213 int val = 1234; | |
214 asm("movss %0, %%xmm0" : "=m"(val)); | |
215 pthread_t tid; | |
216 pthread_create(&tid, NULL, fp_thread, NULL); | |
217 pthread_join(tid, NULL); | |
218 MSG("thread done OK\n"); | |
219 } | |
220 | |
221 static long long read_tsc() { | |
222 long long rc; | |
223 asm volatile( | |
224 "rdtsc\n" | |
225 "mov %%eax, (%0)\n" | |
226 "mov %%edx, 4(%0)\n" | |
227 : | |
228 : "c"(&rc), "a"(-1), "d"(-1)); | |
229 return rc; | |
230 } | |
231 | |
232 TEST(test_rdtsc) { | |
233 StartSeccompSandbox(); | |
234 // Just check that we can do the instruction. | |
235 read_tsc(); | |
236 } | |
237 | |
238 TEST(test_getpid) { | |
239 int pid1 = getpid(); | |
240 StartSeccompSandbox(); | |
241 int pid2 = getpid(); | |
242 assert(pid1 == pid2); | |
243 // Bypass any caching that glibc's getpid() wrapper might do. | |
244 int pid3 = syscall(__NR_getpid); | |
245 assert(pid1 == pid3); | |
246 } | |
247 | |
248 TEST(test_gettid) { | |
249 // glibc doesn't provide a gettid() wrapper. | |
250 int tid1 = syscall(__NR_gettid); | |
251 assert(tid1 > 0); | |
252 StartSeccompSandbox(); | |
253 int tid2 = syscall(__NR_gettid); | |
254 assert(tid1 == tid2); | |
255 } | |
256 | |
257 static void *map_something() { | |
258 void *addr = mmap(NULL, 0x1000, PROT_READ, | |
259 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
260 assert(addr != MAP_FAILED); | |
261 return addr; | |
262 } | |
263 | |
264 TEST(test_mmap_disallows_remapping) { | |
265 void *addr = map_something(); | |
266 StartSeccompSandbox(); | |
267 // Overwriting a mapping that was created before the sandbox was | |
268 // enabled is not allowed. | |
269 void *result = mmap(addr, 0x1000, PROT_READ, | |
270 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | |
271 assert(result == MAP_FAILED); | |
272 assert(errno == EINVAL); | |
273 } | |
274 | |
275 TEST(test_mmap_disallows_low_address) { | |
276 StartSeccompSandbox(); | |
277 // Mapping pages at low addresses is not allowed because this helps | |
278 // with exploiting buggy kernels. | |
279 void *result = mmap(NULL, 0x1000, PROT_READ, | |
280 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | |
281 assert(result == MAP_FAILED); | |
282 assert(errno == EINVAL); | |
283 } | |
284 | |
285 TEST(test_munmap_allowed) { | |
286 StartSeccompSandbox(); | |
287 void *addr = map_something(); | |
288 int result = munmap(addr, 0x1000); | |
289 assert(result == 0); | |
290 } | |
291 | |
292 TEST(test_munmap_disallowed) { | |
293 void *addr = map_something(); | |
294 StartSeccompSandbox(); | |
295 int result = munmap(addr, 0x1000); | |
296 assert(result == -1); | |
297 assert(errno == EINVAL); | |
298 } | |
299 | |
300 TEST(test_mprotect_allowed) { | |
301 StartSeccompSandbox(); | |
302 void *addr = map_something(); | |
303 int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE); | |
304 assert(result == 0); | |
305 } | |
306 | |
307 TEST(test_mprotect_disallowed) { | |
308 void *addr = map_something(); | |
309 StartSeccompSandbox(); | |
310 int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE); | |
311 assert(result == -1); | |
312 assert(errno == EINVAL); | |
313 } | |
314 | |
315 static int get_tty_fd() { | |
316 int master_fd, tty_fd; | |
317 int rc = openpty(&master_fd, &tty_fd, NULL, NULL, NULL); | |
318 assert(rc == 0); | |
319 return tty_fd; | |
320 } | |
321 | |
322 TEST(test_ioctl_tiocgwinsz_allowed) { | |
323 int tty_fd = get_tty_fd(); | |
324 StartSeccompSandbox(); | |
325 int size[2]; | |
326 // Get terminal width and height. | |
327 int result = ioctl(tty_fd, TIOCGWINSZ, size); | |
328 assert(result == 0); | |
329 } | |
330 | |
331 TEST(test_ioctl_disallowed) { | |
332 int tty_fd = get_tty_fd(); | |
333 StartSeccompSandbox(); | |
334 // This ioctl call inserts a character into the tty's input queue, | |
335 // which provides a way to send commands to an interactive shell. | |
336 char c = 'x'; | |
337 int result = ioctl(tty_fd, TIOCSTI, &c); | |
338 assert(result == -1); | |
339 assert(errno == EINVAL); | |
340 } | |
341 | |
342 TEST(test_socket) { | |
343 StartSeccompSandbox(); | |
344 int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
345 assert(fd == -1); | |
346 // TODO: Make it consistent between i386 and x86-64. | |
347 assert(errno == EINVAL || errno == ENOSYS); | |
348 } | |
349 | |
350 TEST(test_open_disabled) { | |
351 StartSeccompSandbox(); | |
352 int fd = open("/dev/null", O_RDONLY); | |
353 assert(fd == -1); | |
354 assert(errno == EACCES); | |
355 | |
356 // Writing to the policy flag does not change this. | |
357 playground::g_policy.allow_file_namespace = true; | |
358 fd = open("/dev/null", O_RDONLY); | |
359 assert(fd == -1); | |
360 assert(errno == EACCES); | |
361 } | |
362 | |
363 TEST(test_open_enabled) { | |
364 playground::g_policy.allow_file_namespace = true; | |
365 StartSeccompSandbox(); | |
366 int fd = open("/dev/null", O_RDONLY); | |
367 assert(fd >= 0); | |
368 int rc = close(fd); | |
369 assert(rc == 0); | |
370 fd = open("/dev/null", O_WRONLY); | |
371 assert(fd == -1); | |
372 assert(errno == EACCES); | |
373 } | |
374 | |
375 TEST(test_access_disabled) { | |
376 StartSeccompSandbox(); | |
377 int rc = access("/dev/null", R_OK); | |
378 assert(rc == -1); | |
379 assert(errno == EACCES); | |
380 } | |
381 | |
382 TEST(test_access_enabled) { | |
383 playground::g_policy.allow_file_namespace = true; | |
384 StartSeccompSandbox(); | |
385 int rc = access("/dev/null", R_OK); | |
386 assert(rc == 0); | |
387 rc = access("path-that-does-not-exist", R_OK); | |
388 assert(rc == -1); | |
389 assert(errno == ENOENT); | |
390 } | |
391 | |
392 TEST(test_stat_disabled) { | |
393 StartSeccompSandbox(); | |
394 struct stat st; | |
395 int rc = stat("/dev/null", &st); | |
396 assert(rc == -1); | |
397 assert(errno == EACCES); | |
398 } | |
399 | |
400 TEST(test_stat_enabled) { | |
401 playground::g_policy.allow_file_namespace = true; | |
402 StartSeccompSandbox(); | |
403 struct stat st; | |
404 int rc = stat("/dev/null", &st); | |
405 assert(rc == 0); | |
406 rc = stat("path-that-does-not-exist", &st); | |
407 assert(rc == -1); | |
408 assert(errno == ENOENT); | |
409 } | |
410 | |
411 static int g_value; | |
412 | |
413 static void signal_handler(int sig) { | |
414 g_value = 300; | |
415 MSG("In signal handler\n"); | |
416 } | |
417 | |
418 static void sigaction_handler(int sig, siginfo_t *a, void *b) { | |
419 g_value = 300; | |
420 MSG("In sigaction handler\n"); | |
421 } | |
422 | |
423 static void (*g_sig_handler_ptr)(int sig, void *addr) asm("g_sig_handler_ptr"); | |
424 | |
425 static void non_fatal_sig_handler(int sig, void *addr) { | |
426 g_value = 300; | |
427 MSG("Caught signal %d at %p\n", sig, addr); | |
428 } | |
429 | |
430 static void fatal_sig_handler(int sig, void *addr) { | |
431 // Recursively trigger another segmentation fault while already in the SEGV | |
432 // handler. This should terminate the program if SIGSEGV is marked as a | |
433 // deferred signal. | |
434 // Only do this on the first entry to this function. Otherwise, the signal | |
435 // handler was probably marked as SA_NODEFER and we want to continue | |
436 // execution. | |
437 if (!g_value++) { | |
438 MSG("Caught signal %d at %p\n", sig, addr); | |
439 if (sig == SIGSEGV) { | |
440 asm volatile("hlt"); | |
441 } else { | |
442 asm volatile("int3"); | |
443 } | |
444 } | |
445 } | |
446 | |
447 static void (*generic_signal_handler(void)) | |
448 (int signo, siginfo_t *info, void *context) { | |
449 void (*hdl)(int, siginfo_t *, void *); | |
450 asm volatile( | |
451 "lea 0f, %0\n" | |
452 "jmp 999f\n" | |
453 "0:\n" | |
454 | |
455 #if defined(__x86_64__) | |
456 "mov 0xB0(%%rsp), %%rsi\n" // Pass original %rip to signal handler | |
457 "cmpb $0xF4, 0(%%rsi)\n" // hlt | |
458 "jnz 1f\n" | |
459 "addq $1, 0xB0(%%rsp)\n" // Adjust %eip past failing instruction | |
460 "1:jmp *g_sig_handler_ptr\n" // Call actual signal handler | |
461 #elif defined(__i386__) | |
462 // TODO(markus): We currently don't guarantee that signal handlers always | |
463 // have the correct "magic" restorer function. If we fix | |
464 // this, we should add a test for it (both for SEGV and | |
465 // non-SEGV). | |
466 "cmpw $0, 0xA(%%esp)\n" | |
467 "lea 0x40(%%esp), %%eax\n" // %eip at time of exception | |
468 "jz 1f\n" | |
469 "add $0x9C, %%eax\n" // %eip at time of exception | |
470 "1:mov 0(%%eax), %%ecx\n" | |
471 "cmpb $0xF4, 0(%%ecx)\n" // hlt | |
472 "jnz 2f\n" | |
473 "addl $1, 0(%%eax)\n" // Adjust %eip past failing instruction | |
474 "2:push %%ecx\n" // Pass original %eip to signal handler | |
475 "mov 8(%%esp), %%eax\n" | |
476 "push %%eax\n" // Pass signal number to signal handler | |
477 "call *g_sig_handler_ptr\n" // Call actual signal handler | |
478 "pop %%eax\n" | |
479 "pop %%ecx\n" | |
480 "ret\n" | |
481 #else | |
482 #error Unsupported target platform | |
483 #endif | |
484 | |
485 "999:\n" | |
486 : "=r"(hdl)); | |
487 return hdl; | |
488 } | |
489 | |
490 TEST(test_signal_handler) { | |
491 sighandler_t result = signal(SIGTRAP, signal_handler); | |
492 assert(result != SIG_ERR); | |
493 | |
494 StartSeccompSandbox(); | |
495 | |
496 result = signal(SIGTRAP, signal_handler); | |
497 assert(result != SIG_ERR); | |
498 | |
499 g_value = 200; | |
500 asm("int3"); | |
501 assert(g_value == 300); | |
502 } | |
503 | |
504 TEST(test_sigaction_handler) { | |
505 struct sigaction act; | |
506 act.sa_sigaction = sigaction_handler; | |
507 sigemptyset(&act.sa_mask); | |
508 act.sa_flags = SA_SIGINFO; | |
509 int rc = sigaction(SIGTRAP, &act, NULL); | |
510 assert(rc == 0); | |
511 | |
512 StartSeccompSandbox(); | |
513 | |
514 rc = sigaction(SIGTRAP, &act, NULL); | |
515 assert(rc == 0); | |
516 | |
517 g_value = 200; | |
518 asm("int3"); | |
519 assert(g_value == 300); | |
520 } | |
521 | |
522 TEST(test_blocked_signal) { | |
523 sighandler_t result = signal(SIGTRAP, signal_handler); | |
524 assert(result != SIG_ERR); | |
525 StartSeccompSandbox(); | |
526 | |
527 // Initially the signal should not be blocked. | |
528 sigset_t sigs; | |
529 sigfillset(&sigs); | |
530 int rc = sigprocmask(0, NULL, &sigs); | |
531 assert(rc == 0); | |
532 assert(!sigismember(&sigs, SIGTRAP)); | |
533 | |
534 sigemptyset(&sigs); | |
535 sigaddset(&sigs, SIGTRAP); | |
536 rc = sigprocmask(SIG_BLOCK, &sigs, NULL); | |
537 assert(rc == 0); | |
538 | |
539 // Check that we can read back the blocked status. | |
540 sigemptyset(&sigs); | |
541 rc = sigprocmask(0, NULL, &sigs); | |
542 assert(rc == 0); | |
543 assert(sigismember(&sigs, SIGTRAP)); | |
544 | |
545 // Check that the signal handler really is blocked. | |
546 intend_exit_status(SIGTRAP, true); | |
547 asm("int3"); | |
548 } | |
549 | |
550 TEST(test_sigaltstack) { | |
551 // The sandbox does not support sigaltstack() yet. Just test that | |
552 // it returns an error. | |
553 StartSeccompSandbox(); | |
554 stack_t st; | |
555 st.ss_size = 0x4000; | |
556 st.ss_sp = malloc(st.ss_size); | |
557 assert(st.ss_sp != NULL); | |
558 st.ss_flags = 0; | |
559 int rc = sigaltstack(&st, NULL); | |
560 assert(rc == -1); | |
561 assert(errno == ENOSYS); | |
562 } | |
563 | |
564 TEST(test_sa_flags) { | |
565 StartSeccompSandbox(); | |
566 int flags[4] = { 0, SA_NODEFER, SA_SIGINFO, SA_SIGINFO | SA_NODEFER }; | |
567 for (int i = 0; i < 4; ++i) { | |
568 struct sigaction sa; | |
569 memset(&sa, 0, sizeof(sa)); | |
570 sa.sa_sigaction = generic_signal_handler(); | |
571 g_sig_handler_ptr = non_fatal_sig_handler; | |
572 sa.sa_flags = flags[i]; | |
573 | |
574 // Test SEGV handling | |
575 g_value = 200; | |
576 sigaction(SIGSEGV, &sa, NULL); | |
577 asm volatile("hlt"); | |
578 assert(g_value == 300); | |
579 | |
580 // Test non-SEGV handling | |
581 g_value = 200; | |
582 sigaction(SIGTRAP, &sa, NULL); | |
583 asm volatile("int3"); | |
584 assert(g_value == 300); | |
585 } | |
586 } | |
587 | |
588 TEST(test_segv_defer) { | |
589 StartSeccompSandbox(); | |
590 struct sigaction sa; | |
591 memset(&sa, 0, sizeof(sa)); | |
592 sa.sa_sigaction = generic_signal_handler(); | |
593 g_sig_handler_ptr = fatal_sig_handler; | |
594 | |
595 // Test non-deferred SEGV (should continue execution) | |
596 sa.sa_flags = SA_NODEFER; | |
597 sigaction(SIGSEGV, &sa, NULL); | |
598 g_value = 0; | |
599 asm volatile("hlt"); | |
600 | |
601 // Test deferred SEGV (should terminate program) | |
602 sa.sa_flags = 0; | |
603 sigaction(SIGSEGV, &sa, NULL); | |
604 g_value = 0; | |
605 intend_exit_status(SIGSEGV, true); | |
606 asm volatile("hlt"); | |
607 } | |
608 | |
609 TEST(test_trap_defer) { | |
610 StartSeccompSandbox(); | |
611 struct sigaction sa; | |
612 memset(&sa, 0, sizeof(sa)); | |
613 sa.sa_sigaction = generic_signal_handler(); | |
614 g_sig_handler_ptr = fatal_sig_handler; | |
615 | |
616 // Test non-deferred TRAP (should continue execution) | |
617 sa.sa_flags = SA_NODEFER; | |
618 sigaction(SIGTRAP, &sa, NULL); | |
619 g_value = 0; | |
620 asm volatile("int3"); | |
621 | |
622 // Test deferred TRAP (should terminate program) | |
623 sa.sa_flags = 0; | |
624 sigaction(SIGTRAP, &sa, NULL); | |
625 g_value = 0; | |
626 intend_exit_status(SIGTRAP, true); | |
627 asm volatile("int3"); | |
628 } | |
629 | |
630 TEST(test_segv_resethand) { | |
631 StartSeccompSandbox(); | |
632 struct sigaction sa; | |
633 memset(&sa, 0, sizeof(sa)); | |
634 sa.sa_sigaction = generic_signal_handler(); | |
635 g_sig_handler_ptr = non_fatal_sig_handler; | |
636 sa.sa_flags = SA_RESETHAND; | |
637 sigaction(SIGSEGV, &sa, NULL); | |
638 | |
639 // Test first invocation of signal handler (should continue execution) | |
640 asm volatile("hlt"); | |
641 | |
642 // Test second invocation of signal handler (should terminate program) | |
643 intend_exit_status(SIGSEGV, true); | |
644 asm volatile("hlt"); | |
645 } | |
646 | |
647 TEST(test_trap_resethand) { | |
648 StartSeccompSandbox(); | |
649 struct sigaction sa; | |
650 memset(&sa, 0, sizeof(sa)); | |
651 sa.sa_sigaction = generic_signal_handler(); | |
652 g_sig_handler_ptr = non_fatal_sig_handler; | |
653 sa.sa_flags = SA_RESETHAND; | |
654 sigaction(SIGTRAP, &sa, NULL); | |
655 | |
656 // Test first invocation of signal handler (should continue execution) | |
657 asm volatile("int3"); | |
658 | |
659 // Test second invocation of signal handler (should terminate program) | |
660 intend_exit_status(SIGTRAP, true); | |
661 asm volatile("int3"); | |
662 } | |
663 | |
664 struct testcase { | |
665 const char *test_name; | |
666 void (*test_func)(); | |
667 }; | |
668 | |
669 struct testcase all_tests[] = { | |
670 #include "test-list.h" | |
671 { NULL, NULL }, | |
672 }; | |
673 | |
674 static int run_test_forked(struct testcase *test) { | |
675 printf("** %s\n", test->test_name); | |
676 int pipe_fds[2]; | |
677 int rc = pipe(pipe_fds); | |
678 assert(rc == 0); | |
679 int pid = fork(); | |
680 if (pid == 0) { | |
681 rc = close(pipe_fds[0]); | |
682 assert(rc == 0); | |
683 g_intended_status_fd = pipe_fds[1]; | |
684 | |
685 test->test_func(); | |
686 intend_exit_status(0, false); | |
687 _exit(0); | |
688 } | |
689 rc = close(pipe_fds[1]); | |
690 assert(rc == 0); | |
691 | |
692 int intended_status; | |
693 int got = read(pipe_fds[0], &intended_status, sizeof(intended_status)); | |
694 bool got_intended_status = got == sizeof(intended_status); | |
695 if (!got_intended_status) { | |
696 printf("Test runner: Did not receive intended status\n"); | |
697 } | |
698 | |
699 int status; | |
700 int pid2 = waitpid(pid, &status, 0); | |
701 assert(pid2 == pid); | |
702 if (!got_intended_status) { | |
703 printf("Test returned exit status %i\n", status); | |
704 return 1; | |
705 } | |
706 else if ((status & ~WCOREFLAG) != intended_status) { | |
707 printf("Test failed with exit status %i, expected %i\n", | |
708 status, intended_status); | |
709 return 1; | |
710 } | |
711 else { | |
712 return 0; | |
713 } | |
714 } | |
715 | |
716 static int run_test_by_name(const char *name) { | |
717 struct testcase *test; | |
718 for (test = all_tests; test->test_name != NULL; test++) { | |
719 if (strcmp(name, test->test_name) == 0) { | |
720 printf("Running test %s...\n", name); | |
721 test->test_func(); | |
722 printf("OK\n"); | |
723 return 0; | |
724 } | |
725 } | |
726 fprintf(stderr, "Test '%s' not found\n", name); | |
727 return 1; | |
728 } | |
729 | |
730 int main(int argc, char **argv) { | |
731 setvbuf(stdout, NULL, _IONBF, 0); | |
732 setvbuf(stderr, NULL, _IONBF, 0); | |
733 if (argc == 2) { | |
734 // Run one test without forking, to aid debugging. | |
735 return run_test_by_name(argv[1]); | |
736 } | |
737 else if (argc > 2) { | |
738 // TODO: run multiple tests. | |
739 fprintf(stderr, "Too many arguments\n"); | |
740 return 1; | |
741 } | |
742 else { | |
743 // Run all tests. | |
744 struct testcase *test; | |
745 int failures = 0; | |
746 for (test = all_tests; test->test_name != NULL; test++) { | |
747 failures += run_test_forked(test); | |
748 } | |
749 if (failures == 0) { | |
750 printf("OK\n"); | |
751 return 0; | |
752 } | |
753 else { | |
754 printf("%i FAILURE(S)\n", failures); | |
755 return 1; | |
756 } | |
757 } | |
758 } | |
OLD | NEW |