1 /* $OpenBSD: dlopen.c,v 1.2 2017/09/07 21:35:35 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
finish(const char * msg,const int * retval,const struct timespec * tsp)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
sigusr1(int sig)146 sigusr1(int sig)
147 {
148 }
149
150 /*
151 * Interrupt via alarm()
152 */
153 void
sigalrm(int sig)154 sigalrm(int sig)
155 {
156 write(1, "* ", 2);
157 }
158
159 void
set_sigalrm(int restart)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
run_sig(const struct test_spec * test)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
run_cancel(const struct test_spec * test)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 *
poll_run(void * arg)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 *
nanosleep_run(void * arg)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
fcntl_init(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 *
fcntl_run(void * arg)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
fcntl_fini(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
flock_init(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 *
flock_run(void * arg)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
flock_fini(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 *
sigsuspend_run(void * arg)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
connect_init(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 *
connect_run(void * arg)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
connect_fini(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
accept_init(void)450 accept_init(void)
451 {
452 accept_fd = socket(AF_INET, SOCK_STREAM, 0);
453 listen(accept_fd, 2);
454 }
455 void *
accept_run(void * arg)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
accept_fini(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
open_fifo_init(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 *
open_fifo_run(void * arg)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
open_fifo_fini(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
semop_init(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 *
semop_run(void * arg)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
semop_fini(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 *
run_tests(void * arg)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
main(int argc,char ** argv)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 (*(void **)f->callback = dlsym(RTLD_DEFAULT, f->name)) == NULL)
668 errx(1, "dlsym %s: %s", f->name, dlerror());
669 }
670
671 /* block SIGALRM in the original thread */
672 sigemptyset(&mask);
673 sigaddset(&mask, SIGALRM);
674 sigprocmask(SIG_BLOCK, &mask, NULL);
675
676 printf("in thread after dlopen(pthread)\n");
677 if ((r = p_create(&t, NULL, run_tests, &tests)))
678 errc(1, r, "pthread_create");
679 if ((r = p_join(t, &ret)))
680 errc(1, r, "pthread_join");
681 if (ret != &tests)
682 errx(1, "bad return by thread: %p != %p", ret, (void *)&tests);
683
684
685 /*
686 * Run the tests again, this time using cancellation
687 */
688 printf("using cancellation\n");
689 run = run_cancel;
690 run_tests(&tests);
691
692 if (dlclose(handle))
693 errx(1, "dlclose: %s", dlerror());
694
695 return 0;
696 }
697