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