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