| 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 |