OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <assert.h> | 5 #include <assert.h> |
| 6 #include <dirent.h> |
6 #include <pthread.h> | 7 #include <pthread.h> |
7 #include <pty.h> | 8 #include <pty.h> |
8 #include <sys/types.h> | 9 #include <sys/types.h> |
9 #include <sys/wait.h> | 10 #include <sys/wait.h> |
10 | 11 |
11 #include "sandbox_impl.h" | 12 #include "sandbox_impl.h" |
12 | 13 |
13 | 14 |
14 // This is basically a marker to grep for. | 15 // This is basically a marker to grep for. |
15 #define TEST(name) void name() | 16 #define TEST(name) void name() |
16 | 17 |
| 18 TEST(test_dup) { |
| 19 StartSeccompSandbox(); |
| 20 // Test a simple syscall that is marked as UNRESTRICTED_SYSCALL. |
| 21 int fd = dup(1); |
| 22 assert(fd >= 0); |
| 23 int rc = close(fd); |
| 24 assert(rc == 0); |
| 25 } |
| 26 |
| 27 // This has an off-by-three error because it counts ".", "..", and the |
| 28 // FD for the /proc/self/fd directory. This doesn't matter because it |
| 29 // is only used to check for differences in the number of open FDs. |
| 30 int count_fds() { |
| 31 DIR *dir = opendir("/proc/self/fd"); |
| 32 assert(dir != NULL); |
| 33 int count = 0; |
| 34 while (1) { |
| 35 struct dirent *d = readdir(dir); |
| 36 if (d == NULL) |
| 37 break; |
| 38 count++; |
| 39 } |
| 40 int rc = closedir(dir); |
| 41 assert(rc == 0); |
| 42 return count; |
| 43 } |
| 44 |
17 void *thread_func(void *x) { | 45 void *thread_func(void *x) { |
18 int *ptr = (int *) x; | 46 int *ptr = (int *) x; |
19 *ptr = 123; | 47 *ptr = 123; |
20 printf("In new thread\n"); | 48 printf("In new thread\n"); |
21 return (void *) 456; | 49 return (void *) 456; |
22 } | 50 } |
23 | 51 |
24 TEST(test_thread) { | 52 TEST(test_thread) { |
25 StartSeccompSandbox(); | 53 StartSeccompSandbox(); |
| 54 int fd_count1 = count_fds(); |
26 pthread_t tid; | 55 pthread_t tid; |
27 int x; | 56 int x = 999; |
28 void *result; | 57 void *result; |
29 pthread_create(&tid, NULL, thread_func, &x); | 58 pthread_create(&tid, NULL, thread_func, &x); |
30 printf("Waiting for thread\n"); | 59 printf("Waiting for thread\n"); |
31 pthread_join(tid, &result); | 60 pthread_join(tid, &result); |
32 assert(result == (void *) 456); | 61 assert(result == (void *) 456); |
33 assert(x == 123); | 62 assert(x == 123); |
| 63 // Check that the process has not leaked FDs. |
| 64 int fd_count2 = count_fds(); |
| 65 assert(fd_count2 == fd_count1); |
34 } | 66 } |
35 | 67 |
36 int clone_func(void *x) { | 68 int clone_func(void *x) { |
| 69 int *ptr = (int *) x; |
| 70 *ptr = 124; |
| 71 printf("In thread\n"); |
| 72 // On x86-64, returning from this function calls the __NR_exit_group |
| 73 // syscall instead of __NR_exit. |
| 74 syscall(__NR_exit, 100); |
| 75 // Not reached. |
| 76 return 200; |
| 77 } |
| 78 |
| 79 #if defined(__i386__) |
| 80 int get_gs() { |
| 81 int gs; |
| 82 asm volatile("mov %%gs, %0" : "=r"(gs)); |
| 83 return gs; |
| 84 } |
| 85 #endif |
| 86 |
| 87 void *get_tls_base() { |
| 88 void *base; |
| 89 #if defined(__x86_64__) |
| 90 asm volatile("mov %%fs:0, %0" : "=r"(base)); |
| 91 #elif defined(__i386__) |
| 92 asm volatile("mov %%gs:0, %0" : "=r"(base)); |
| 93 #else |
| 94 #error Unsupported target platform |
| 95 #endif |
| 96 return base; |
| 97 } |
| 98 |
| 99 TEST(test_clone) { |
| 100 StartSeccompSandbox(); |
| 101 int fd_count1 = count_fds(); |
| 102 int stack_size = 0x1000; |
| 103 char *stack = (char *) malloc(stack_size); |
| 104 assert(stack != NULL); |
| 105 int flags = CLONE_VM | CLONE_FS | CLONE_FILES | |
| 106 CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | |
| 107 CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; |
| 108 int tid = -1; |
| 109 int x = 999; |
| 110 |
| 111 // The sandbox requires us to pass CLONE_TLS. Pass settings that |
| 112 // are enough to copy the parent thread's TLS setup. This allows us |
| 113 // to invoke libc in the child thread. |
| 114 #if defined(__x86_64__) |
| 115 void *tls = get_tls_base(); |
| 116 #elif defined(__i386__) |
| 117 struct user_desc tls_desc, *tls = &tls_desc; |
| 118 tls_desc.entry_number = get_gs() >> 3; |
| 119 tls_desc.base_addr = (long) get_tls_base(); |
| 120 tls_desc.limit = 0xfffff; |
| 121 tls_desc.seg_32bit = 1; |
| 122 tls_desc.contents = 0; |
| 123 tls_desc.read_exec_only = 0; |
| 124 tls_desc.limit_in_pages = 1; |
| 125 tls_desc.seg_not_present = 0; |
| 126 tls_desc.useable = 1; |
| 127 #else |
| 128 #error Unsupported target platform |
| 129 #endif |
| 130 |
| 131 int rc = clone(clone_func, (void *) (stack + stack_size), flags, &x, |
| 132 &tid, tls, &tid); |
| 133 assert(rc > 0); |
| 134 while (tid == rc) { |
| 135 syscall(__NR_futex, &tid, FUTEX_WAIT, rc, NULL); |
| 136 } |
| 137 assert(tid == 0); |
| 138 assert(x == 124); |
| 139 // Check that the process has not leaked FDs. |
| 140 int fd_count2 = count_fds(); |
| 141 assert(fd_count2 == fd_count1); |
| 142 } |
| 143 |
| 144 int uncalled_clone_func(void *x) { |
37 printf("In thread func, which shouldn't happen\n"); | 145 printf("In thread func, which shouldn't happen\n"); |
38 return 1; | 146 return 1; |
39 } | 147 } |
40 | 148 |
41 TEST(test_clone_disallowed_flags) { | 149 TEST(test_clone_disallowed_flags) { |
42 StartSeccompSandbox(); | 150 StartSeccompSandbox(); |
43 int stack_size = 4096; | 151 int stack_size = 4096; |
44 char *stack = (char *) malloc(stack_size); | 152 char *stack = (char *) malloc(stack_size); |
45 assert(stack != NULL); | 153 assert(stack != NULL); |
46 /* We omit the flags CLONE_SETTLS, CLONE_PARENT_SETTID and | 154 /* We omit the flags CLONE_SETTLS, CLONE_PARENT_SETTID and |
47 CLONE_CHILD_CLEARTID, which is disallowed by the sandbox. */ | 155 CLONE_CHILD_CLEARTID, which is disallowed by the sandbox. */ |
48 int flags = CLONE_VM | CLONE_FS | CLONE_FILES | | 156 int flags = CLONE_VM | CLONE_FS | CLONE_FILES | |
49 CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; | 157 CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; |
50 int rc = clone(clone_func, (void *) (stack + stack_size), flags, NULL, | 158 int rc = clone(uncalled_clone_func, (void *) (stack + stack_size), |
51 NULL, NULL, NULL); | 159 flags, NULL, NULL, NULL, NULL); |
52 assert(rc == -1); | 160 assert(rc == -1); |
53 assert(errno == EPERM); | 161 assert(errno == EPERM); |
54 } | 162 } |
55 | 163 |
56 long long read_tsc() { | 164 long long read_tsc() { |
57 long long rc; | 165 long long rc; |
58 asm volatile( | 166 asm volatile( |
59 "rdtsc\n" | 167 "rdtsc\n" |
60 "mov %%eax, (%0)\n" | 168 "mov %%eax, (%0)\n" |
61 "mov %%edx, 4(%0)\n" | 169 "mov %%edx, 4(%0)\n" |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 struct testcase { | 326 struct testcase { |
219 const char *test_name; | 327 const char *test_name; |
220 void (*test_func)(); | 328 void (*test_func)(); |
221 }; | 329 }; |
222 | 330 |
223 struct testcase all_tests[] = { | 331 struct testcase all_tests[] = { |
224 #include "test-list.h" | 332 #include "test-list.h" |
225 { NULL, NULL }, | 333 { NULL, NULL }, |
226 }; | 334 }; |
227 | 335 |
228 void run_test_forked(struct testcase *test) { | 336 int run_test_forked(struct testcase *test) { |
229 printf("** %s\n", test->test_name); | 337 printf("** %s\n", test->test_name); |
230 int pid = fork(); | 338 int pid = fork(); |
231 if (pid == 0) { | 339 if (pid == 0) { |
232 test->test_func(); | 340 test->test_func(); |
233 _exit(0); | 341 _exit(0); |
234 } | 342 } |
235 int status; | 343 int status; |
236 waitpid(pid, &status, 0); | 344 waitpid(pid, &status, 0); |
237 if (status != 0) { | 345 if (status != 0) { |
238 printf("Test failed with exit status %i\n", status); | 346 printf("Test failed with exit status %i\n", status); |
239 exit(1); | 347 return 1; |
| 348 } |
| 349 else { |
| 350 return 0; |
240 } | 351 } |
241 } | 352 } |
242 | 353 |
243 int run_test_by_name(const char *name) { | 354 int run_test_by_name(const char *name) { |
244 struct testcase *test; | 355 struct testcase *test; |
245 for (test = all_tests; test->test_name != NULL; test++) { | 356 for (test = all_tests; test->test_name != NULL; test++) { |
246 if (strcmp(name, test->test_name) == 0) { | 357 if (strcmp(name, test->test_name) == 0) { |
247 test->test_func(); | 358 test->test_func(); |
248 return 0; | 359 return 0; |
249 } | 360 } |
250 } | 361 } |
251 fprintf(stderr, "Test '%s' not found\n", name); | 362 fprintf(stderr, "Test '%s' not found\n", name); |
252 return 1; | 363 return 1; |
253 } | 364 } |
254 | 365 |
255 int main(int argc, char **argv) { | 366 int main(int argc, char **argv) { |
256 if (argc == 2) { | 367 if (argc == 2) { |
257 // Run one test without forking, to aid debugging. | 368 // Run one test without forking, to aid debugging. |
258 return run_test_by_name(argv[1]); | 369 return run_test_by_name(argv[1]); |
259 } | 370 } |
260 else if (argc > 2) { | 371 else if (argc > 2) { |
261 // TODO: run multiple tests. | 372 // TODO: run multiple tests. |
262 fprintf(stderr, "Too many arguments\n"); | 373 fprintf(stderr, "Too many arguments\n"); |
263 return 1; | 374 return 1; |
264 } | 375 } |
265 else { | 376 else { |
266 // Run all tests. | 377 // Run all tests. |
267 struct testcase *test; | 378 struct testcase *test; |
| 379 int failures = 0; |
268 for (test = all_tests; test->test_name != NULL; test++) { | 380 for (test = all_tests; test->test_name != NULL; test++) { |
269 run_test_forked(test); | 381 failures += run_test_forked(test); |
| 382 } |
| 383 if (failures == 0) { |
| 384 printf("OK\n"); |
| 385 return 0; |
| 386 } |
| 387 else { |
| 388 printf("%i FAILURE(S)\n", failures); |
| 389 return 1; |
270 } | 390 } |
271 } | 391 } |
272 return 0; | |
273 } | 392 } |
OLD | NEW |