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