1 /* $OpenBSD: dlopen.c,v 1.1 2016/05/10 03:59:55 guenther Exp $ */ 2 /* 3 * Copyright (c) 2016 Philip Guenther <guenther@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * Test that various calls can be interrupted in a non-threaded process, 20 * then dlopen() libpthread and do that again in a second thread, 21 * and then verify that they're all correctly acting as cancellation points. 22 */ 23 24 #include <sys/types.h> 25 #include <sys/ipc.h> 26 #include <sys/sem.h> 27 #include <sys/socket.h> 28 #include <sys/stat.h> 29 #include <sys/time.h> 30 #include <sys/wait.h> 31 32 #include <arpa/inet.h> 33 #include <netinet/in.h> 34 #include <netinet/tcp.h> 35 36 #include <dlfcn.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <poll.h> 41 #include <pthread.h> 42 #include <signal.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <time.h> 47 #include <unistd.h> 48 49 /* path of fifo we remove/create/open/remove */ 50 #define FIFO_PATH "fifo" 51 52 /* path of lock file remove/create/lock/remove */ 53 #define LOCK_PATH "lock" 54 55 #define TEST_ACCEPT 0x001 56 #define TEST_CONNECT 0x002 57 #define TEST_FCNTL 0x004 58 #define TEST_FLOCK 0x008 59 #define TEST_NANOSLEEP 0x010 60 #define TEST_OPEN_FIFO 0x020 61 #define TEST_POLL 0x040 62 #define TEST_SIGSUSPEND 0x080 63 #define TEST_SEMOP 0x100 64 65 #define TEST_ALL 0x1ff 66 67 struct test_spec 68 { 69 int flag; 70 const char *name; 71 void (*init)(void); 72 void *(*run)(void *); 73 void (*fini)(void); 74 }; 75 76 77 /* 78 * Functions looked up in libpthread 79 */ 80 int (*p_attr_init)(pthread_attr_t *); 81 int (*p_attr_setdetachstate)(pthread_attr_t *, int); 82 int (*p_cancel)(pthread_t); 83 int (*p_cond_destroy)(pthread_cond_t *); 84 int (*p_cond_timedwait)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *); 85 int (*p_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); 86 int (*p_join)(pthread_t, void **); 87 int (*p_mutex_destroy)(pthread_mutex_t *); 88 int (*p_mutex_lock)(pthread_mutex_t *); 89 int (*p_mutex_unlock)(pthread_mutex_t *); 90 pthread_t (*p_self)(void); 91 92 struct funcs 93 { 94 const char *name; 95 void *callback; 96 } functions[] = 97 { 98 #define FUNC(f) { "pthread_"#f, &p_##f } 99 FUNC(attr_init), 100 FUNC(attr_setdetachstate), 101 FUNC(cancel), 102 FUNC(cond_destroy), 103 FUNC(cond_timedwait), 104 FUNC(create), 105 FUNC(join), 106 FUNC(mutex_destroy), 107 FUNC(mutex_lock), 108 FUNC(mutex_unlock), 109 FUNC(self), 110 { NULL, NULL } 111 #undef FUNC 112 }; 113 114 /* 115 * Shared cleanup 116 */ 117 void 118 finish(const char *msg, const int *retval, const struct timespec *tsp) 119 { 120 struct timespec after; 121 const char *fill = "\t\t\t"; 122 123 clock_gettime(CLOCK_REALTIME, &after); 124 after.tv_sec -= tsp->tv_sec; 125 after.tv_nsec -= tsp->tv_nsec; 126 if (after.tv_nsec < 0) { 127 after.tv_sec--; 128 after.tv_nsec += 1000000000L; 129 } 130 131 fill += (strlen(msg) - 1) / 8; 132 if (retval[0] >= 0) 133 printf("%s: fail%s\ttime = %ld.%09lu\nr = %d\n", 134 msg, fill, (long)after.tv_sec, after.tv_nsec, retval[0]); 135 else if (retval[1] != EINTR) 136 printf("%s: fail%s\ttime = %ld.%09lu\nr = %d\terrno = %d: %s\n", 137 msg, fill, (long)after.tv_sec, after.tv_nsec, 138 retval[0], retval[1], strerror(retval[1])); 139 else 140 printf("%s: pass%s\ttime = %ld.%09lu\n", 141 msg, fill, (long)after.tv_sec, after.tv_nsec); 142 } 143 144 /* noop signal handler */ 145 void 146 sigusr1(int sig) 147 { 148 } 149 150 /* 151 * Interrupt via alarm() 152 */ 153 void 154 sigalrm(int sig) 155 { 156 write(1, "* ", 2); 157 } 158 159 void 160 set_sigalrm(int restart) 161 { 162 struct sigaction sa; 163 164 sa.sa_handler = &sigalrm; 165 sa.sa_flags = restart ? SA_RESTART : 0; 166 sigemptyset(&sa.sa_mask); 167 sigaction(SIGALRM, &sa, NULL); 168 } 169 170 void 171 run_sig(const struct test_spec *test) 172 { 173 struct timespec before; 174 int retval[2]; 175 176 if (test->init != NULL) 177 test->init(); 178 if (clock_gettime(CLOCK_REALTIME, &before)) 179 err(1, "clock_gettime"); 180 alarm(1); 181 test->run(retval); 182 finish(test->name, retval, &before); 183 if (test->fini != NULL) 184 test->fini(); 185 } 186 187 188 /* 189 * Interrupt via cancellation 190 */ 191 192 193 void 194 run_cancel(const struct test_spec *test) 195 { 196 struct timespec before, target_time; 197 pthread_t tester; 198 pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 199 pthread_cond_t c = PTHREAD_COND_INITIALIZER; 200 int retval[2]; 201 int r; 202 203 if (test->init != NULL) 204 test->init(); 205 206 if ((r = p_mutex_lock(&m))) 207 errc(1, r, "pthread_mutex_lock"); 208 209 if (clock_gettime(CLOCK_REALTIME, &before)) 210 err(1, "clock_gettime"); 211 212 target_time.tv_sec = before.tv_sec + 1; 213 target_time.tv_nsec = before.tv_nsec; 214 215 retval[0] = -2; 216 if ((r = p_create(&tester, NULL, test->run, retval))) 217 errc(1, r, "pthread_create"); 218 219 /* overkill: could have done it with pthread_mutex_timedlock */ 220 do 221 r = p_cond_timedwait(&c, &m, &target_time); 222 while (r == 0); 223 if (r != ETIMEDOUT) 224 errc(1, r, "pthread_cond_timedwait"); 225 write(1, "* ", 2); 226 if (retval[0] == -2) { 227 retval[0] = -1; 228 retval[1] = EINTR; 229 } 230 if ((r = p_cancel(tester))) 231 errc(1, r, "pthread_cancel"); 232 if ((r = p_mutex_unlock(&m))) 233 errc(1, r, "pthread_mutex_unlock"); 234 if ((r = p_mutex_destroy(&m))) 235 errc(1, r, "pthread_mutex_destroy"); 236 if ((r = p_cond_destroy(&c))) 237 errc(1, r, "pthread_cond_destroy"); 238 239 finish(test->name, retval, &before); 240 if (test->fini != NULL) 241 test->fini(); 242 } 243 244 void (*run)(const struct test_spec *_test) = run_sig; 245 246 247 /* 248 * The operations that are exercised in the tests 249 */ 250 251 /* 252 * POLL 253 */ 254 void * 255 poll_run(void *arg) 256 { 257 struct pollfd pfd; 258 int *retval = arg; 259 260 pfd.fd = 0; 261 pfd.events = POLLIN; 262 retval[0] = poll(&pfd, 1, 3 * 1000); 263 retval[1] = errno; 264 return NULL; 265 } 266 267 /* 268 * NANOSLEEP 269 */ 270 void * 271 nanosleep_run(void *arg) 272 { 273 struct timespec ts; 274 int *retval = arg; 275 276 ts.tv_sec = 2; 277 ts.tv_nsec = 0; 278 retval[0] = nanosleep(&ts, &ts); 279 retval[1] = errno; 280 return NULL; 281 } 282 283 /* 284 * FCNTL 285 */ 286 struct flock fcntl_fl = { 287 .l_start = 0, 288 .l_len = 0, 289 .l_type = F_WRLCK, 290 .l_whence = SEEK_SET, 291 }; 292 static int fcntl_fd = -1; 293 static pid_t fcntl_pid = 0; 294 void 295 fcntl_init(void) 296 { 297 int fds[2]; 298 char buf[1]; 299 300 if (unlink(LOCK_PATH) && errno != ENOENT) 301 err(1, "unlink %s", LOCK_PATH); 302 if (pipe(fds)) 303 err(1, "pipe"); 304 fcntl_fd = open(LOCK_PATH, O_RDWR | O_CREAT, 0666); 305 fcntl_pid = fork(); 306 if (fcntl_pid == 0) { 307 fcntl(fcntl_fd, F_SETLKW, &fcntl_fl); 308 close(fds[0]); 309 close(fds[1]); 310 sleep(1000); 311 _exit(0); 312 } 313 close(fds[1]); 314 read(fds[0], buf, 1); 315 close(fds[0]); 316 } 317 void * 318 fcntl_run(void *arg) 319 { 320 int *retval = arg; 321 322 retval[0] = fcntl(fcntl_fd, F_SETLKW, &fcntl_fl); 323 retval[1] = errno; 324 return NULL; 325 } 326 void 327 fcntl_fini(void) 328 { 329 if (fcntl_fd >= 0) { 330 close(fcntl_fd); 331 fcntl_fd = -1; 332 } 333 if (fcntl_pid > 0) { 334 kill(fcntl_pid, SIGINT); 335 waitpid(fcntl_pid, NULL, 0); 336 fcntl_pid = 0; 337 } 338 if (unlink(LOCK_PATH)) 339 err(1, "unlink %s", LOCK_PATH); 340 } 341 342 /* 343 * FLOCK 344 */ 345 static int flock_fd = -1; 346 static pid_t flock_pid = 0; 347 void 348 flock_init(void) 349 { 350 int fds[2]; 351 char buf[1]; 352 353 if (unlink(LOCK_PATH) && errno != ENOENT) 354 err(1, "unlink %s", LOCK_PATH); 355 if (pipe(fds)) 356 err(1, "pipe"); 357 flock_pid = fork(); 358 flock_fd = open(LOCK_PATH, O_RDWR | O_CREAT, 0666); 359 if (flock_pid == 0) { 360 flock(flock_fd, LOCK_EX); 361 close(fds[0]); 362 close(fds[1]); 363 sleep(1000); 364 _exit(0); 365 } 366 close(fds[1]); 367 read(fds[0], buf, 1); 368 close(fds[0]); 369 } 370 void * 371 flock_run(void *arg) 372 { 373 int *retval = arg; 374 375 retval[0] = flock(flock_fd, LOCK_EX); 376 retval[1] = errno; 377 return NULL; 378 } 379 void 380 flock_fini(void) 381 { 382 if (flock_fd >= 0) { 383 close(flock_fd); 384 flock_fd = -1; 385 } 386 if (flock_pid > 0) { 387 kill(flock_pid, SIGINT); 388 waitpid(flock_pid, NULL, 0); 389 flock_pid = 0; 390 } 391 if (unlink(LOCK_PATH) && errno != ENOENT) 392 err(1, "unlink %s", LOCK_PATH); 393 } 394 395 /* 396 * SIGSUSPEND 397 */ 398 void * 399 sigsuspend_run(void *arg) 400 { 401 sigset_t set; 402 int *retval = arg; 403 404 sigemptyset(&set); 405 retval[0] = sigsuspend(&set); 406 retval[1] = errno; 407 return NULL; 408 } 409 410 /* 411 * CONNECT 412 */ 413 static int connect_fd = -1; 414 void 415 connect_init(void) 416 { 417 int on = 1; 418 419 connect_fd = socket(AF_INET, SOCK_STREAM, 0); 420 setsockopt(connect_fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); 421 setsockopt(connect_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); 422 } 423 void * 424 connect_run(void *arg) 425 { 426 struct sockaddr_in sin; 427 int *retval = arg; 428 429 sin.sin_family = AF_INET; 430 inet_pton(AF_INET, "223.255.255.255", &sin.sin_addr); 431 sin.sin_port = 25; 432 retval[0] = connect(connect_fd, (struct sockaddr *)&sin, sizeof(sin)); 433 retval[1] = errno; 434 return NULL; 435 } 436 void 437 connect_fini(void) 438 { 439 if (connect_fd >= 0) { 440 close(connect_fd); 441 connect_fd = -1; 442 } 443 } 444 445 /* 446 * ACCEPT 447 */ 448 static int accept_fd = -1; 449 void 450 accept_init(void) 451 { 452 accept_fd = socket(AF_INET, SOCK_STREAM, 0); 453 listen(accept_fd, 2); 454 } 455 void * 456 accept_run(void *arg) 457 { 458 struct sockaddr_in sin; 459 socklen_t sl; 460 int *retval = arg; 461 462 sl = sizeof(sin); 463 retval[0] = accept(accept_fd, (struct sockaddr *)&sin, &sl); 464 retval[1] = errno; 465 return NULL; 466 } 467 void 468 accept_fini(void) 469 { 470 if (accept_fd >= 0) { 471 close(accept_fd); 472 accept_fd = -1; 473 } 474 } 475 476 /* 477 * OPEN FIFO 478 */ 479 void 480 open_fifo_init(void) 481 { 482 /* let's get a fresh fifo */ 483 if (unlink(FIFO_PATH) && errno != ENOENT) 484 err(1, "unlink %s", FIFO_PATH); 485 if (mkfifo(FIFO_PATH, 0600)) 486 err(1, "mkfifo %s", FIFO_PATH); 487 } 488 void * 489 open_fifo_run(void *arg) 490 { 491 int *retval = arg; 492 493 retval[0] = open(FIFO_PATH, O_RDONLY); 494 retval[1] = errno; 495 return NULL; 496 } 497 void 498 open_fifo_fini(void) 499 { 500 if (unlink(FIFO_PATH) && errno != ENOENT) 501 err(1, "unlink %s", FIFO_PATH); 502 } 503 504 /* 505 * SEMOP 506 */ 507 static int semid = -1; 508 void 509 semop_init(void) 510 { 511 union { 512 int val; 513 struct semid_ds *buf; 514 unsigned short *array; 515 } semarg; 516 unsigned short val; 517 518 semid = semget(IPC_PRIVATE, 1, 0600); 519 semarg.array = &val; 520 val = 0; 521 semctl(semid, 0, SETALL, semarg); 522 } 523 void * 524 semop_run(void *arg) 525 { 526 struct sembuf op; 527 int *retval = arg; 528 529 op.sem_num = 0; 530 op.sem_op = -1; 531 op.sem_flg = 0; 532 retval[0] = semop(semid, &op, 1); 533 retval[1] = errno; 534 return NULL; 535 } 536 void 537 semop_fini(void) 538 { 539 if (semid >= 0) { 540 semctl(semid, 0, IPC_RMID, NULL); 541 semid = -1; 542 } 543 } 544 545 #define TESTSPEC_FULL(flag, name, prefix) \ 546 { flag, name, prefix##_init, prefix##_run, prefix##_fini } 547 #define TESTSPEC(flag, name, prefix) \ 548 { flag, name, NULL, prefix##_run, NULL } 549 struct test_spec test_specs[] = { 550 TESTSPEC_FULL(TEST_ACCEPT, "accept", accept), 551 TESTSPEC_FULL(TEST_CONNECT, "connect", connect), 552 TESTSPEC_FULL(TEST_FCNTL, "fcntl(F_SETLKW)", fcntl), 553 TESTSPEC_FULL(TEST_SEMOP, "semop", semop), 554 TESTSPEC_FULL(TEST_FLOCK, "flock", flock), 555 TESTSPEC_FULL(TEST_OPEN_FIFO, "open_fifo", open_fifo), 556 557 TESTSPEC(TEST_NANOSLEEP, "nanosleep", nanosleep), 558 TESTSPEC(TEST_POLL, "poll", poll), 559 TESTSPEC(TEST_SIGSUSPEND, "sigsuspend", sigsuspend), 560 { 0 } 561 }; 562 563 564 void * 565 run_tests(void *arg) 566 { 567 int tests = *(int *)arg; 568 int flag; 569 struct test_spec *test; 570 sigset_t mask; 571 572 /* make sure SIGALRM is unblocked for the tests */ 573 sigemptyset(&mask); 574 sigaddset(&mask, SIGALRM); 575 sigprocmask(SIG_UNBLOCK, &mask, NULL); 576 577 while (tests > 0) { 578 flag = tests & ~(tests >> 1); 579 tests &= ~flag; 580 for (test = test_specs; test->flag; test++) 581 if (test->flag == flag) { 582 run(test); 583 break; 584 } 585 } 586 587 return arg; 588 } 589 590 int 591 main(int argc, char **argv) 592 { 593 int ch, tests; 594 sigset_t mask; 595 int r; 596 void *handle; 597 struct funcs *f; 598 pthread_t t; 599 void *ret; 600 601 set_sigalrm(0); 602 603 tests = 0; 604 while ((ch = getopt(argc, argv, "AacFfinoprSs")) != -1) 605 switch (ch) { 606 case 'A': 607 tests |= TEST_ALL; 608 break; 609 case 'a': 610 tests |= TEST_ACCEPT; 611 break; 612 case 'c': 613 tests |= TEST_CONNECT; 614 break; 615 case 'F': 616 tests |= TEST_FCNTL; 617 break; 618 case 'f': 619 tests |= TEST_FLOCK; 620 break; 621 case 'i': 622 set_sigalrm(0); 623 break; 624 case 'n': 625 tests |= TEST_NANOSLEEP; 626 break; 627 case 'o': 628 tests |= TEST_OPEN_FIFO; 629 break; 630 case 'p': 631 tests |= TEST_POLL; 632 break; 633 case 'r': 634 set_sigalrm(1); 635 break; 636 case 's': 637 tests |= TEST_SIGSUSPEND; 638 break; 639 case 'S': 640 tests |= TEST_SEMOP; 641 break; 642 } 643 if (tests == 0) 644 tests = TEST_ALL; 645 646 /* make sure SIGTERM is unblocked */ 647 sigemptyset(&mask); 648 sigaddset(&mask, SIGTERM); 649 sigprocmask(SIG_UNBLOCK, &mask, NULL); 650 651 /* 652 * Run them in the original thread 653 */ 654 printf("single threaded\n"); 655 run_tests(&tests); 656 657 658 /* 659 * Open libpthread, create a thread and run them in *that* 660 */ 661 if ((handle = dlopen("libpthread.so", RTLD_LAZY)) == NULL) 662 errx(1, "dlopen: %s", dlerror()); 663 664 /* look up all the functions. The cast here isn't strictly portable */ 665 for (f = functions; f->name != NULL; f++) { 666 if ((*(void **)f->callback = dlsym(handle, f->name)) == NULL) 667 errx(1, "dlsym %s: %s", f->name, dlerror()); 668 } 669 670 /* block SIGALRM in the original thread */ 671 sigemptyset(&mask); 672 sigaddset(&mask, SIGALRM); 673 sigprocmask(SIG_BLOCK, &mask, NULL); 674 675 printf("in thread after dlopen(pthread)\n"); 676 if ((r = p_create(&t, NULL, run_tests, &tests))) 677 errc(1, r, "pthread_create"); 678 if ((r = p_join(t, &ret))) 679 errc(1, r, "pthread_join"); 680 if (ret != &tests) 681 errx(1, "bad return by thread: %p != %p", ret, (void *)&tests); 682 683 684 /* 685 * Run the tests again, this time using cancellation 686 */ 687 printf("using cancellation\n"); 688 run = run_cancel; 689 run_tests(&tests); 690 691 if (dlclose(handle)) 692 errx(1, "dlclose: %s", dlerror()); 693 694 return 0; 695 } 696