xref: /freebsd/tools/regression/posixsem/posixsem.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2008 Yahoo!, Inc.
5  * All rights reserved.
6  * Written by: John Baldwin <jhb@FreeBSD.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/queue.h>
38 #include <sys/_semaphore.h>
39 #include <sys/sysctl.h>
40 #include <sys/time.h>
41 #include <sys/user.h>
42 #include <sys/wait.h>
43 
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <kvm.h>
47 #include <limits.h>
48 #include <semaphore.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <time.h>
54 #include <unistd.h>
55 
56 #include "test.h"
57 
58 #define	TEST_PATH	"/tmp/posixsem_regression_test"
59 
60 #define	ELAPSED(elapsed, limit)		(abs((elapsed) - (limit)) < 100)
61 
62 /* Macros for passing child status to parent over a pipe. */
63 #define	CSTAT(class, error)		((class) << 16 | (error))
64 #define	CSTAT_CLASS(stat)		((stat) >> 16)
65 #define	CSTAT_ERROR(stat)		((stat) & 0xffff)
66 
67 /*
68  * Helper routine for tests that use a child process.  This routine
69  * creates a pipe and forks a child process.  The child process runs
70  * the 'func' routine which returns a status integer.  The status
71  * integer gets written over the pipe to the parent and returned in
72  * '*stat'.  If there is an error in pipe(), fork(), or wait() this
73  * returns -1 and fails the test.
74  */
75 static int
76 child_worker(int (*func)(void *arg), void *arg, int *stat)
77 {
78 	pid_t pid;
79 	int pfd[2], cstat;
80 
81 	if (pipe(pfd) < 0) {
82 		fail_errno("pipe");
83 		return (-1);
84 	}
85 
86 	pid = fork();
87 	switch (pid) {
88 	case -1:
89 		/* Error. */
90 		fail_errno("fork");
91 		close(pfd[0]);
92 		close(pfd[1]);
93 		return (-1);
94 	case 0:
95 		/* Child. */
96 		cstat = func(arg);
97 		write(pfd[1], &cstat, sizeof(cstat));
98 		exit(0);
99 	}
100 
101 	if (read(pfd[0], stat, sizeof(*stat)) < 0) {
102 		fail_errno("read(pipe)");
103 		close(pfd[0]);
104 		close(pfd[1]);
105 		return (-1);
106 	}
107 	if (waitpid(pid, NULL, 0) < 0) {
108 		fail_errno("wait");
109 		close(pfd[0]);
110 		close(pfd[1]);
111 		return (-1);
112 	}
113 	close(pfd[0]);
114 	close(pfd[1]);
115 	return (0);
116 }
117 
118 /*
119  * Attempt a ksem_open() that should fail with an expected error of
120  * 'error'.
121  */
122 static void
123 ksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
124     value, int error)
125 {
126 	semid_t id;
127 
128 	if (ksem_open(&id, path, flags, mode, value) >= 0) {
129 		fail_err("ksem_open() didn't fail");
130 		ksem_close(id);
131 		return;
132 	}
133 	if (errno != error) {
134 		fail_errno("ksem_open");
135 		return;
136 	}
137 	pass();
138 }
139 
140 /*
141  * Attempt a ksem_unlink() that should fail with an expected error of
142  * 'error'.
143  */
144 static void
145 ksem_unlink_should_fail(const char *path, int error)
146 {
147 
148 	if (ksem_unlink(path) >= 0) {
149 		fail_err("ksem_unlink() didn't fail");
150 		return;
151 	}
152 	if (errno != error) {
153 		fail_errno("ksem_unlink");
154 		return;
155 	}
156 	pass();
157 }
158 
159 /*
160  * Attempt a ksem_close() that should fail with an expected error of
161  * 'error'.
162  */
163 static void
164 ksem_close_should_fail(semid_t id, int error)
165 {
166 
167 	if (ksem_close(id) >= 0) {
168 		fail_err("ksem_close() didn't fail");
169 		return;
170 	}
171 	if (errno != error) {
172 		fail_errno("ksem_close");
173 		return;
174 	}
175 	pass();
176 }
177 
178 /*
179  * Attempt a ksem_init() that should fail with an expected error of
180  * 'error'.
181  */
182 static void
183 ksem_init_should_fail(unsigned int value, int error)
184 {
185 	semid_t id;
186 
187 	if (ksem_init(&id, value) >= 0) {
188 		fail_err("ksem_init() didn't fail");
189 		ksem_destroy(id);
190 		return;
191 	}
192 	if (errno != error) {
193 		fail_errno("ksem_init");
194 		return;
195 	}
196 	pass();
197 }
198 
199 /*
200  * Attempt a ksem_destroy() that should fail with an expected error of
201  * 'error'.
202  */
203 static void
204 ksem_destroy_should_fail(semid_t id, int error)
205 {
206 
207 	if (ksem_destroy(id) >= 0) {
208 		fail_err("ksem_destroy() didn't fail");
209 		return;
210 	}
211 	if (errno != error) {
212 		fail_errno("ksem_destroy");
213 		return;
214 	}
215 	pass();
216 }
217 
218 /*
219  * Attempt a ksem_post() that should fail with an expected error of
220  * 'error'.
221  */
222 static void
223 ksem_post_should_fail(semid_t id, int error)
224 {
225 
226 	if (ksem_post(id) >= 0) {
227 		fail_err("ksem_post() didn't fail");
228 		return;
229 	}
230 	if (errno != error) {
231 		fail_errno("ksem_post");
232 		return;
233 	}
234 	pass();
235 }
236 
237 static void
238 open_after_unlink(void)
239 {
240 	semid_t id;
241 
242 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
243 		fail_errno("ksem_open(1)");
244 		return;
245 	}
246 	ksem_close(id);
247 
248 	if (ksem_unlink(TEST_PATH) < 0) {
249 		fail_errno("ksem_unlink");
250 		return;
251 	}
252 
253 	ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
254 }
255 TEST(open_after_unlink, "open after unlink");
256 
257 static void
258 open_invalid_path(void)
259 {
260 
261 	ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
262 }
263 TEST(open_invalid_path, "open invalid path");
264 
265 static void
266 open_extra_flags(void)
267 {
268 
269 	ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
270 }
271 TEST(open_extra_flags, "open with extra flags");
272 
273 static void
274 open_bad_value(void)
275 {
276 
277 	(void)ksem_unlink(TEST_PATH);
278 
279 	ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
280 }
281 TEST(open_bad_value, "open with invalid initial value");
282 
283 static void
284 open_bad_path_pointer(void)
285 {
286 
287 	ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
288 }
289 TEST(open_bad_path_pointer, "open bad path pointer");
290 
291 static void
292 open_path_too_long(void)
293 {
294 	char *page;
295 
296 	page = malloc(MAXPATHLEN + 1);
297 	memset(page, 'a', MAXPATHLEN);
298 	page[MAXPATHLEN] = '\0';
299 	ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
300 	free(page);
301 }
302 TEST(open_path_too_long, "open pathname too long");
303 
304 static void
305 open_nonexisting_semaphore(void)
306 {
307 
308 	ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
309 }
310 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
311 
312 static void
313 exclusive_create_existing_semaphore(void)
314 {
315 	semid_t id;
316 
317 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
318 		fail_errno("ksem_open(O_CREAT)");
319 		return;
320 	}
321 	ksem_close(id);
322 
323 	ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
324 
325 	ksem_unlink(TEST_PATH);
326 }
327 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
328 
329 static void
330 init_bad_value(void)
331 {
332 
333 	ksem_init_should_fail(UINT_MAX, EINVAL);
334 }
335 TEST(init_bad_value, "init with invalid initial value");
336 
337 static void
338 unlink_bad_path_pointer(void)
339 {
340 
341 	ksem_unlink_should_fail((char *)1024, EFAULT);
342 }
343 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
344 
345 static void
346 unlink_path_too_long(void)
347 {
348 	char *page;
349 
350 	page = malloc(MAXPATHLEN + 1);
351 	memset(page, 'a', MAXPATHLEN);
352 	page[MAXPATHLEN] = '\0';
353 	ksem_unlink_should_fail(page, ENAMETOOLONG);
354 	free(page);
355 }
356 TEST(unlink_path_too_long, "unlink pathname too long");
357 
358 static void
359 destroy_named_semaphore(void)
360 {
361 	semid_t id;
362 
363 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
364 		fail_errno("ksem_open(O_CREAT)");
365 		return;
366 	}
367 
368 	ksem_destroy_should_fail(id, EINVAL);
369 
370 	ksem_close(id);
371 	ksem_unlink(TEST_PATH);
372 }
373 TEST(destroy_named_semaphore, "destroy named semaphore");
374 
375 static void
376 close_unnamed_semaphore(void)
377 {
378 	semid_t id;
379 
380 	if (ksem_init(&id, 1) < 0) {
381 		fail_errno("ksem_init");
382 		return;
383 	}
384 
385 	ksem_close_should_fail(id, EINVAL);
386 
387 	ksem_destroy(id);
388 }
389 TEST(close_unnamed_semaphore, "close unnamed semaphore");
390 
391 static void
392 destroy_invalid_fd(void)
393 {
394 
395 	ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
396 }
397 TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
398 
399 static void
400 close_invalid_fd(void)
401 {
402 
403 	ksem_close_should_fail(STDERR_FILENO, EINVAL);
404 }
405 TEST(close_invalid_fd, "close non-semaphore file descriptor");
406 
407 static void
408 create_unnamed_semaphore(void)
409 {
410 	semid_t id;
411 
412 	if (ksem_init(&id, 1) < 0) {
413 		fail_errno("ksem_init");
414 		return;
415 	}
416 
417 	if (ksem_destroy(id) < 0) {
418 		fail_errno("ksem_destroy");
419 		return;
420 	}
421 	pass();
422 }
423 TEST(create_unnamed_semaphore, "create unnamed semaphore");
424 
425 static void
426 open_named_semaphore(void)
427 {
428 	semid_t id;
429 
430 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
431 		fail_errno("ksem_open(O_CREAT)");
432 		return;
433 	}
434 
435 	if (ksem_close(id) < 0) {
436 		fail_errno("ksem_close");
437 		return;
438 	}
439 
440 	if (ksem_unlink(TEST_PATH) < 0) {
441 		fail_errno("ksem_unlink");
442 		return;
443 	}
444 	pass();
445 }
446 TEST(open_named_semaphore, "create named semaphore");
447 
448 static void
449 getvalue_invalid_semaphore(void)
450 {
451 	int val;
452 
453 	if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
454 		fail_err("ksem_getvalue() didn't fail");
455 		return;
456 	}
457 	if (errno != EINVAL) {
458 		fail_errno("ksem_getvalue");
459 		return;
460 	}
461 	pass();
462 }
463 TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
464 
465 static void
466 post_invalid_semaphore(void)
467 {
468 
469 	ksem_post_should_fail(STDERR_FILENO, EINVAL);
470 }
471 TEST(post_invalid_semaphore, "post of invalid semaphore");
472 
473 static void
474 wait_invalid_semaphore(void)
475 {
476 
477 	if (ksem_wait(STDERR_FILENO) >= 0) {
478 		fail_err("ksem_wait() didn't fail");
479 		return;
480 	}
481 	if (errno != EINVAL) {
482 		fail_errno("ksem_wait");
483 		return;
484 	}
485 	pass();
486 }
487 TEST(wait_invalid_semaphore, "wait for invalid semaphore");
488 
489 static void
490 trywait_invalid_semaphore(void)
491 {
492 
493 	if (ksem_trywait(STDERR_FILENO) >= 0) {
494 		fail_err("ksem_trywait() didn't fail");
495 		return;
496 	}
497 	if (errno != EINVAL) {
498 		fail_errno("ksem_trywait");
499 		return;
500 	}
501 	pass();
502 }
503 TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
504 
505 static void
506 timedwait_invalid_semaphore(void)
507 {
508 
509 	if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
510 		fail_err("ksem_timedwait() didn't fail");
511 		return;
512 	}
513 	if (errno != EINVAL) {
514 		fail_errno("ksem_timedwait");
515 		return;
516 	}
517 	pass();
518 }
519 TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
520 
521 static int
522 checkvalue(semid_t id, int expected)
523 {
524 	int val;
525 
526 	if (ksem_getvalue(id, &val) < 0) {
527 		fail_errno("ksem_getvalue");
528 		return (-1);
529 	}
530 	if (val != expected) {
531 		fail_err("sem value should be %d instead of %d", expected, val);
532 		return (-1);
533 	}
534 	return (0);
535 }
536 
537 static void
538 post_test(void)
539 {
540 	semid_t id;
541 
542 	if (ksem_init(&id, 1) < 0) {
543 		fail_errno("ksem_init");
544 		return;
545 	}
546 	if (checkvalue(id, 1) < 0) {
547 		ksem_destroy(id);
548 		return;
549 	}
550 	if (ksem_post(id) < 0) {
551 		fail_errno("ksem_post");
552 		ksem_destroy(id);
553 		return;
554 	}
555 	if (checkvalue(id, 2) < 0) {
556 		ksem_destroy(id);
557 		return;
558 	}
559 	if (ksem_destroy(id) < 0) {
560 		fail_errno("ksem_destroy");
561 		return;
562 	}
563 	pass();
564 }
565 TEST(post_test, "simple post");
566 
567 static void
568 use_after_unlink_test(void)
569 {
570 	semid_t id;
571 
572 	/*
573 	 * Create named semaphore with value of 1 and then unlink it
574 	 * while still retaining the initial reference.
575 	 */
576 	if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
577 		fail_errno("ksem_open(O_CREAT | O_EXCL)");
578 		return;
579 	}
580 	if (ksem_unlink(TEST_PATH) < 0) {
581 		fail_errno("ksem_unlink");
582 		ksem_close(id);
583 		return;
584 	}
585 	if (checkvalue(id, 1) < 0) {
586 		ksem_close(id);
587 		return;
588 	}
589 
590 	/* Post the semaphore to set its value to 2. */
591 	if (ksem_post(id) < 0) {
592 		fail_errno("ksem_post");
593 		ksem_close(id);
594 		return;
595 	}
596 	if (checkvalue(id, 2) < 0) {
597 		ksem_close(id);
598 		return;
599 	}
600 
601 	/* Wait on the semaphore which should set its value to 1. */
602 	if (ksem_wait(id) < 0) {
603 		fail_errno("ksem_wait");
604 		ksem_close(id);
605 		return;
606 	}
607 	if (checkvalue(id, 1) < 0) {
608 		ksem_close(id);
609 		return;
610 	}
611 
612 	if (ksem_close(id) < 0) {
613 		fail_errno("ksem_close");
614 		return;
615 	}
616 	pass();
617 }
618 TEST(use_after_unlink_test, "use named semaphore after unlink");
619 
620 static void
621 unlocked_trywait(void)
622 {
623 	semid_t id;
624 
625 	if (ksem_init(&id, 1) < 0) {
626 		fail_errno("ksem_init");
627 		return;
628 	}
629 
630 	/* This should succeed and decrement the value to 0. */
631 	if (ksem_trywait(id) < 0) {
632 		fail_errno("ksem_trywait()");
633 		ksem_destroy(id);
634 		return;
635 	}
636 	if (checkvalue(id, 0) < 0) {
637 		ksem_destroy(id);
638 		return;
639 	}
640 
641 	if (ksem_destroy(id) < 0) {
642 		fail_errno("ksem_destroy");
643 		return;
644 	}
645 	pass();
646 }
647 TEST(unlocked_trywait, "unlocked trywait");
648 
649 static void
650 locked_trywait(void)
651 {
652 	semid_t id;
653 
654 	if (ksem_init(&id, 0) < 0) {
655 		fail_errno("ksem_init");
656 		return;
657 	}
658 
659 	/* This should fail with EAGAIN and leave the value at 0. */
660 	if (ksem_trywait(id) >= 0) {
661 		fail_err("ksem_trywait() didn't fail");
662 		ksem_destroy(id);
663 		return;
664 	}
665 	if (errno != EAGAIN) {
666 		fail_errno("wrong error from ksem_trywait()");
667 		ksem_destroy(id);
668 		return;
669 	}
670 	if (checkvalue(id, 0) < 0) {
671 		ksem_destroy(id);
672 		return;
673 	}
674 
675 	if (ksem_destroy(id) < 0) {
676 		fail_errno("ksem_destroy");
677 		return;
678 	}
679 	pass();
680 }
681 TEST(locked_trywait, "locked trywait");
682 
683 /*
684  * Use a timer to post a specific semaphore after a timeout.  A timer
685  * is scheduled via schedule_post().  check_alarm() must be called
686  * afterwards to clean up and check for errors.
687  */
688 static semid_t alarm_id = -1;
689 static int alarm_errno;
690 static int alarm_handler_installed;
691 
692 static void
693 alarm_handler(int signo)
694 {
695 
696 	if (ksem_post(alarm_id) < 0)
697 		alarm_errno = errno;
698 }
699 
700 static int
701 check_alarm(int just_clear)
702 {
703 	struct itimerval it;
704 
705 	bzero(&it, sizeof(it));
706 	if (just_clear) {
707 		setitimer(ITIMER_REAL, &it, NULL);
708 		alarm_errno = 0;
709 		alarm_id = -1;
710 		return (0);
711 	}
712 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
713 		fail_errno("setitimer");
714 		return (-1);
715 	}
716 	if (alarm_errno != 0 && !just_clear) {
717 		errno = alarm_errno;
718 		fail_errno("ksem_post() (via timeout)");
719 		alarm_errno = 0;
720 		return (-1);
721 	}
722 	alarm_id = -1;
723 
724 	return (0);
725 }
726 
727 static int
728 schedule_post(semid_t id, u_int msec)
729 {
730 	struct itimerval it;
731 
732 	if (!alarm_handler_installed) {
733 		if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
734 			fail_errno("signal(SIGALRM)");
735 			return (-1);
736 		}
737 		alarm_handler_installed = 1;
738 	}
739 	if (alarm_id != -1) {
740 		fail_err("ksem_post() already scheduled");
741 		return (-1);
742 	}
743 	alarm_id = id;
744 	bzero(&it, sizeof(it));
745 	it.it_value.tv_sec = msec / 1000;
746 	it.it_value.tv_usec = (msec % 1000) * 1000;
747 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
748 		fail_errno("setitimer");
749 		return (-1);
750 	}
751 	return (0);
752 }
753 
754 static int
755 timedwait(semid_t id, u_int msec, u_int *delta, int error)
756 {
757 	struct timespec start, end;
758 
759 	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
760 		fail_errno("clock_gettime(CLOCK_REALTIME)");
761 		return (-1);
762 	}
763 	end.tv_sec = msec / 1000;
764 	end.tv_nsec = msec % 1000 * 1000000;
765 	timespecadd(&end, &start, &end);
766 	if (ksem_timedwait(id, &end) < 0) {
767 		if (errno != error) {
768 			fail_errno("ksem_timedwait");
769 			return (-1);
770 		}
771 	} else if (error != 0) {
772 		fail_err("ksem_timedwait() didn't fail");
773 		return (-1);
774 	}
775 	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
776 		fail_errno("clock_gettime(CLOCK_REALTIME)");
777 		return (-1);
778 	}
779 	timespecsub(&end, &start, &end);
780 	*delta = end.tv_nsec / 1000000;
781 	*delta += end.tv_sec * 1000;
782 	return (0);
783 }
784 
785 static void
786 unlocked_timedwait(void)
787 {
788 	semid_t id;
789 	u_int elapsed;
790 
791 	if (ksem_init(&id, 1) < 0) {
792 		fail_errno("ksem_init");
793 		return;
794 	}
795 
796 	/* This should succeed right away and set the value to 0. */
797 	if (timedwait(id, 5000, &elapsed, 0) < 0) {
798 		ksem_destroy(id);
799 		return;
800 	}
801 	if (!ELAPSED(elapsed, 0)) {
802 		fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
803 		ksem_destroy(id);
804 		return;
805 	}
806 	if (checkvalue(id, 0) < 0) {
807 		ksem_destroy(id);
808 		return;
809 	}
810 
811 	if (ksem_destroy(id) < 0) {
812 		fail_errno("ksem_destroy");
813 		return;
814 	}
815 	pass();
816 }
817 TEST(unlocked_timedwait, "unlocked timedwait");
818 
819 static void
820 expired_timedwait(void)
821 {
822 	semid_t id;
823 	u_int elapsed;
824 
825 	if (ksem_init(&id, 0) < 0) {
826 		fail_errno("ksem_init");
827 		return;
828 	}
829 
830 	/* This should fail with a timeout and leave the value at 0. */
831 	if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
832 		ksem_destroy(id);
833 		return;
834 	}
835 	if (!ELAPSED(elapsed, 2500)) {
836 		fail_err(
837 	    "ksem_timedwait() of locked sem took %ums instead of 2500ms",
838 		    elapsed);
839 		ksem_destroy(id);
840 		return;
841 	}
842 	if (checkvalue(id, 0) < 0) {
843 		ksem_destroy(id);
844 		return;
845 	}
846 
847 	if (ksem_destroy(id) < 0) {
848 		fail_errno("ksem_destroy");
849 		return;
850 	}
851 	pass();
852 }
853 TEST(expired_timedwait, "locked timedwait timeout");
854 
855 static void
856 locked_timedwait(void)
857 {
858 	semid_t id;
859 	u_int elapsed;
860 
861 	if (ksem_init(&id, 0) < 0) {
862 		fail_errno("ksem_init");
863 		return;
864 	}
865 
866 	/*
867 	 * Schedule a post to trigger after 1000 ms.  The subsequent
868 	 * timedwait should succeed after 1000 ms as a result w/o
869 	 * timing out.
870 	 */
871 	if (schedule_post(id, 1000) < 0) {
872 		ksem_destroy(id);
873 		return;
874 	}
875 	if (timedwait(id, 2000, &elapsed, 0) < 0) {
876 		check_alarm(1);
877 		ksem_destroy(id);
878 		return;
879 	}
880 	if (!ELAPSED(elapsed, 1000)) {
881 		fail_err(
882 	    "ksem_timedwait() with delayed post took %ums instead of 1000ms",
883 		    elapsed);
884 		check_alarm(1);
885 		ksem_destroy(id);
886 		return;
887 	}
888 	if (check_alarm(0) < 0) {
889 		ksem_destroy(id);
890 		return;
891 	}
892 
893 	if (ksem_destroy(id) < 0) {
894 		fail_errno("ksem_destroy");
895 		return;
896 	}
897 	pass();
898 }
899 TEST(locked_timedwait, "locked timedwait");
900 
901 static int
902 testwait(semid_t id, u_int *delta)
903 {
904 	struct timespec start, end;
905 
906 	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
907 		fail_errno("clock_gettime(CLOCK_REALTIME)");
908 		return (-1);
909 	}
910 	if (ksem_wait(id) < 0) {
911 		fail_errno("ksem_wait");
912 		return (-1);
913 	}
914 	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
915 		fail_errno("clock_gettime(CLOCK_REALTIME)");
916 		return (-1);
917 	}
918 	timespecsub(&end, &start, &end);
919 	*delta = end.tv_nsec / 1000000;
920 	*delta += end.tv_sec * 1000;
921 	return (0);
922 }
923 
924 static void
925 unlocked_wait(void)
926 {
927 	semid_t id;
928 	u_int elapsed;
929 
930 	if (ksem_init(&id, 1) < 0) {
931 		fail_errno("ksem_init");
932 		return;
933 	}
934 
935 	/* This should succeed right away and set the value to 0. */
936 	if (testwait(id, &elapsed) < 0) {
937 		ksem_destroy(id);
938 		return;
939 	}
940 	if (!ELAPSED(elapsed, 0)) {
941 		fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
942 		ksem_destroy(id);
943 		return;
944 	}
945 	if (checkvalue(id, 0) < 0) {
946 		ksem_destroy(id);
947 		return;
948 	}
949 
950 	if (ksem_destroy(id) < 0) {
951 		fail_errno("ksem_destroy");
952 		return;
953 	}
954 	pass();
955 }
956 TEST(unlocked_wait, "unlocked wait");
957 
958 static void
959 locked_wait(void)
960 {
961 	semid_t id;
962 	u_int elapsed;
963 
964 	if (ksem_init(&id, 0) < 0) {
965 		fail_errno("ksem_init");
966 		return;
967 	}
968 
969 	/*
970 	 * Schedule a post to trigger after 1000 ms.  The subsequent
971 	 * wait should succeed after 1000 ms as a result.
972 	 */
973 	if (schedule_post(id, 1000) < 0) {
974 		ksem_destroy(id);
975 		return;
976 	}
977 	if (testwait(id, &elapsed) < 0) {
978 		check_alarm(1);
979 		ksem_destroy(id);
980 		return;
981 	}
982 	if (!ELAPSED(elapsed, 1000)) {
983 		fail_err(
984 	    "ksem_wait() with delayed post took %ums instead of 1000ms",
985 		    elapsed);
986 		check_alarm(1);
987 		ksem_destroy(id);
988 		return;
989 	}
990 	if (check_alarm(0) < 0) {
991 		ksem_destroy(id);
992 		return;
993 	}
994 
995 	if (ksem_destroy(id) < 0) {
996 		fail_errno("ksem_destroy");
997 		return;
998 	}
999 	pass();
1000 }
1001 TEST(locked_wait, "locked wait");
1002 
1003 /*
1004  * Fork off a child process.  The child will open the semaphore via
1005  * the same name.  The child will then block on the semaphore waiting
1006  * for the parent to post it.
1007  */
1008 static int
1009 wait_twoproc_child(void *arg)
1010 {
1011 	semid_t id;
1012 
1013 	if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
1014 		return (CSTAT(1, errno));
1015 	if (ksem_wait(id) < 0)
1016 		return (CSTAT(2, errno));
1017 	if (ksem_close(id) < 0)
1018 		return (CSTAT(3, errno));
1019 	return (CSTAT(0, 0));
1020 }
1021 
1022 static void
1023 wait_twoproc_test(void)
1024 {
1025 	semid_t id;
1026 	int stat;
1027 
1028 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
1029 		fail_errno("ksem_open");
1030 		return;
1031 	}
1032 
1033 	if (schedule_post(id, 500) < 0) {
1034 		ksem_close(id);
1035 		ksem_unlink(TEST_PATH);
1036 		return;
1037 	}
1038 
1039 	if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
1040 		check_alarm(1);
1041 		ksem_close(id);
1042 		ksem_unlink(TEST_PATH);
1043 		return;
1044 	}
1045 
1046 	errno = CSTAT_ERROR(stat);
1047 	switch (CSTAT_CLASS(stat)) {
1048 	case 0:
1049 		pass();
1050 		break;
1051 	case 1:
1052 		fail_errno("child ksem_open()");
1053 		break;
1054 	case 2:
1055 		fail_errno("child ksem_wait()");
1056 		break;
1057 	case 3:
1058 		fail_errno("child ksem_close()");
1059 		break;
1060 	default:
1061 		fail_err("bad child state %#x", stat);
1062 		break;
1063 	}
1064 
1065 	check_alarm(1);
1066 	ksem_close(id);
1067 	ksem_unlink(TEST_PATH);
1068 }
1069 TEST(wait_twoproc_test, "two proc wait");
1070 
1071 static void
1072 maxvalue_test(void)
1073 {
1074 	semid_t id;
1075 	int val;
1076 
1077 	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1078 		fail_errno("ksem_init");
1079 		return;
1080 	}
1081 	if (ksem_getvalue(id, &val) < 0) {
1082 		fail_errno("ksem_getvalue");
1083 		ksem_destroy(id);
1084 		return;
1085 	}
1086 	if (val != SEM_VALUE_MAX) {
1087 		fail_err("value %d != SEM_VALUE_MAX");
1088 		ksem_destroy(id);
1089 		return;
1090 	}
1091 	if (val < 0) {
1092 		fail_err("value < 0");
1093 		ksem_destroy(id);
1094 		return;
1095 	}
1096 	if (ksem_destroy(id) < 0) {
1097 		fail_errno("ksem_destroy");
1098 		return;
1099 	}
1100 	pass();
1101 }
1102 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1103 
1104 static void
1105 maxvalue_post_test(void)
1106 {
1107 	semid_t id;
1108 
1109 	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1110 		fail_errno("ksem_init");
1111 		return;
1112 	}
1113 
1114 	ksem_post_should_fail(id, EOVERFLOW);
1115 
1116 	ksem_destroy(id);
1117 }
1118 TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1119 
1120 static void
1121 busy_destroy_test(void)
1122 {
1123 	char errbuf[_POSIX2_LINE_MAX];
1124 	struct kinfo_proc *kp;
1125 	semid_t id;
1126 	pid_t pid;
1127 	kvm_t *kd;
1128 	int count;
1129 
1130 	kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
1131 	if (kd == NULL) {
1132 		fail_err("kvm_openfiles: %s", errbuf);
1133 		return;
1134 	}
1135 
1136 	if (ksem_init(&id, 0) < 0) {
1137 		fail_errno("ksem_init");
1138 		kvm_close(kd);
1139 		return;
1140 	}
1141 
1142 	pid = fork();
1143 	switch (pid) {
1144 	case -1:
1145 		/* Error. */
1146 		fail_errno("fork");
1147 		ksem_destroy(id);
1148 		kvm_close(kd);
1149 		return;
1150 	case 0:
1151 		/* Child. */
1152 		ksem_wait(id);
1153 		exit(0);
1154 	}
1155 
1156 	/*
1157 	 * Wait for the child process to block on the semaphore.  This
1158 	 * is a bit gross.
1159 	 */
1160 	for (;;) {
1161 		kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
1162 		if (kp == NULL) {
1163 			fail_err("kvm_getprocs: %s", kvm_geterr(kd));
1164 			kvm_close(kd);
1165 			ksem_destroy(id);
1166 			return;
1167 		}
1168 		if (kp->ki_stat == SSLEEP &&
1169 		    (strcmp(kp->ki_wmesg, "sem") == 0 ||
1170 		    strcmp(kp->ki_wmesg, "ksem") == 0))
1171 			break;
1172 		usleep(1000);
1173 	}
1174 	kvm_close(kd);
1175 
1176 	ksem_destroy_should_fail(id, EBUSY);
1177 
1178 	/* Cleanup. */
1179 	ksem_post(id);
1180 	waitpid(pid, NULL, 0);
1181 	ksem_destroy(id);
1182 }
1183 TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1184 
1185 static int
1186 exhaust_unnamed_child(void *arg)
1187 {
1188 	semid_t id;
1189 	int i, max;
1190 
1191 	max = (intptr_t)arg;
1192 	for (i = 0; i < max + 1; i++) {
1193 		if (ksem_init(&id, 1) < 0) {
1194 			if (errno == ENOSPC)
1195 				return (CSTAT(0, 0));
1196 			return (CSTAT(1, errno));
1197 		}
1198 	}
1199 	return (CSTAT(2, 0));
1200 }
1201 
1202 static void
1203 exhaust_unnamed_sems(void)
1204 {
1205 	size_t len;
1206 	int nsems_max, stat;
1207 
1208 	len = sizeof(nsems_max);
1209 	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1210 	    0) {
1211 		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1212 		return;
1213 	}
1214 
1215 	if (child_worker(exhaust_unnamed_child, (void *)(uintptr_t)nsems_max,
1216 	    &stat))
1217 		return;
1218 	errno = CSTAT_ERROR(stat);
1219 	switch (CSTAT_CLASS(stat)) {
1220 	case 0:
1221 		pass();
1222 		break;
1223 	case 1:
1224 		fail_errno("ksem_init");
1225 		break;
1226 	case 2:
1227 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1228 		break;
1229 	default:
1230 		fail_err("bad child state %#x", stat);
1231 		break;
1232 	}
1233 }
1234 TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1235 
1236 static int
1237 exhaust_named_child(void *arg)
1238 {
1239 	char buffer[64];
1240 	semid_t id;
1241 	int i, max;
1242 
1243 	max = (intptr_t)arg;
1244 	for (i = 0; i < max + 1; i++) {
1245 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1246 		if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
1247 			if (errno == ENOSPC || errno == EMFILE ||
1248 			    errno == ENFILE)
1249 				return (CSTAT(0, 0));
1250 			return (CSTAT(1, errno));
1251 		}
1252 	}
1253 	return (CSTAT(2, errno));
1254 }
1255 
1256 static void
1257 exhaust_named_sems(void)
1258 {
1259 	char buffer[64];
1260 	size_t len;
1261 	int i, nsems_max, stat;
1262 
1263 	len = sizeof(nsems_max);
1264 	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1265 	    0) {
1266 		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1267 		return;
1268 	}
1269 
1270 	if (child_worker(exhaust_named_child, (void *)(uintptr_t)nsems_max,
1271 	    &stat) < 0)
1272 		return;
1273 	errno = CSTAT_ERROR(stat);
1274 	switch (CSTAT_CLASS(stat)) {
1275 	case 0:
1276 		pass();
1277 		break;
1278 	case 1:
1279 		fail_errno("ksem_open");
1280 		break;
1281 	case 2:
1282 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1283 		break;
1284 	default:
1285 		fail_err("bad child state %#x", stat);
1286 		break;
1287 	}
1288 
1289 	/* Cleanup any semaphores created by the child. */
1290 	for (i = 0; i < nsems_max + 1; i++) {
1291 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1292 		ksem_unlink(buffer);
1293 	}
1294 }
1295 TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1296 
1297 static int
1298 fdlimit_set(void *arg)
1299 {
1300 	struct rlimit rlim;
1301 	int max;
1302 
1303 	max = (intptr_t)arg;
1304 	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
1305 		return (CSTAT(3, errno));
1306 	rlim.rlim_cur = max;
1307 	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
1308 		return (CSTAT(4, errno));
1309 	return (0);
1310 }
1311 
1312 static int
1313 fdlimit_unnamed_child(void *arg)
1314 {
1315 	int stat;
1316 
1317 	stat = fdlimit_set(arg);
1318 	if (stat == 0)
1319 		stat = exhaust_unnamed_child(arg);
1320 	return (stat);
1321 }
1322 
1323 static void
1324 fdlimit_unnamed_sems(void)
1325 {
1326 	int nsems_max, stat;
1327 
1328 	nsems_max = 10;
1329 	if (child_worker(fdlimit_unnamed_child, (void *)(uintptr_t)nsems_max,
1330 	    &stat))
1331 		return;
1332 	errno = CSTAT_ERROR(stat);
1333 	switch (CSTAT_CLASS(stat)) {
1334 	case 0:
1335 		pass();
1336 		break;
1337 	case 1:
1338 		fail_errno("ksem_init");
1339 		break;
1340 	case 2:
1341 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1342 		break;
1343 	case 3:
1344 		fail_errno("getrlimit");
1345 		break;
1346 	case 4:
1347 		fail_errno("getrlimit");
1348 		break;
1349 	default:
1350 		fail_err("bad child state %#x", stat);
1351 		break;
1352 	}
1353 }
1354 TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1355 
1356 static int
1357 fdlimit_named_child(void *arg)
1358 {
1359 	int stat;
1360 
1361 	stat = fdlimit_set(arg);
1362 	if (stat == 0)
1363 		stat = exhaust_named_child(arg);
1364 	return (stat);
1365 }
1366 
1367 static void
1368 fdlimit_named_sems(void)
1369 {
1370 	char buffer[64];
1371 	int i, nsems_max, stat;
1372 
1373 	nsems_max = 10;
1374 	if (child_worker(fdlimit_named_child, (void *)(uintptr_t)nsems_max,
1375 	    &stat) < 0)
1376 		return;
1377 	errno = CSTAT_ERROR(stat);
1378 	switch (CSTAT_CLASS(stat)) {
1379 	case 0:
1380 		pass();
1381 		break;
1382 	case 1:
1383 		fail_errno("ksem_open");
1384 		break;
1385 	case 2:
1386 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1387 		break;
1388 	case 3:
1389 		fail_errno("getrlimit");
1390 		break;
1391 	case 4:
1392 		fail_errno("getrlimit");
1393 		break;
1394 	default:
1395 		fail_err("bad child state %#x", stat);
1396 		break;
1397 	}
1398 
1399 	/* Cleanup any semaphores created by the child. */
1400 	for (i = 0; i < nsems_max + 1; i++) {
1401 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1402 		ksem_unlink(buffer);
1403 	}
1404 }
1405 TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1406 
1407 int
1408 main(int argc, char *argv[])
1409 {
1410 
1411 	signal(SIGSYS, SIG_IGN);
1412 	run_tests();
1413 	return (0);
1414 }
1415