xref: /openbsd/regress/lib/libpthread/dlopen/dlopen.c (revision 264ca280)
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