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