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 */
72 static int
child_worker(int (* func)(void * arg),void * arg,int * stat)73 child_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 */
119 static void
ksem_open_should_fail(const char * path,int flags,mode_t mode,unsigned int value,int error)120 ksem_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 */
141 static void
ksem_unlink_should_fail(const char * path,int error)142 ksem_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 */
160 static void
ksem_close_should_fail(semid_t id,int error)161 ksem_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 */
179 static void
ksem_init_should_fail(unsigned int value,int error)180 ksem_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 */
200 static void
ksem_destroy_should_fail(semid_t id,int error)201 ksem_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 */
219 static void
ksem_post_should_fail(semid_t id,int error)220 ksem_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
234 static void
open_after_unlink(void)235 open_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 }
252 TEST(open_after_unlink, "open after unlink");
253
254 static void
open_invalid_path(void)255 open_invalid_path(void)
256 {
257
258 ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
259 }
260 TEST(open_invalid_path, "open invalid path");
261
262 static void
open_extra_flags(void)263 open_extra_flags(void)
264 {
265
266 ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
267 }
268 TEST(open_extra_flags, "open with extra flags");
269
270 static void
open_bad_value(void)271 open_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 }
278 TEST(open_bad_value, "open with invalid initial value");
279
280 static void
open_bad_path_pointer(void)281 open_bad_path_pointer(void)
282 {
283
284 ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
285 }
286 TEST(open_bad_path_pointer, "open bad path pointer");
287
288 static void
open_path_too_long(void)289 open_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 }
299 TEST(open_path_too_long, "open pathname too long");
300
301 static void
open_nonexisting_semaphore(void)302 open_nonexisting_semaphore(void)
303 {
304
305 ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
306 }
307 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
308
309 static void
exclusive_create_existing_semaphore(void)310 exclusive_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 }
324 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
325
326 static void
init_bad_value(void)327 init_bad_value(void)
328 {
329
330 ksem_init_should_fail(UINT_MAX, EINVAL);
331 }
332 TEST(init_bad_value, "init with invalid initial value");
333
334 static void
unlink_bad_path_pointer(void)335 unlink_bad_path_pointer(void)
336 {
337
338 ksem_unlink_should_fail((char *)1024, EFAULT);
339 }
340 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
341
342 static void
unlink_path_too_long(void)343 unlink_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 }
353 TEST(unlink_path_too_long, "unlink pathname too long");
354
355 static void
destroy_named_semaphore(void)356 destroy_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 }
370 TEST(destroy_named_semaphore, "destroy named semaphore");
371
372 static void
close_unnamed_semaphore(void)373 close_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 }
386 TEST(close_unnamed_semaphore, "close unnamed semaphore");
387
388 static void
destroy_invalid_fd(void)389 destroy_invalid_fd(void)
390 {
391
392 ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
393 }
394 TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
395
396 static void
close_invalid_fd(void)397 close_invalid_fd(void)
398 {
399
400 ksem_close_should_fail(STDERR_FILENO, EINVAL);
401 }
402 TEST(close_invalid_fd, "close non-semaphore file descriptor");
403
404 static void
create_unnamed_semaphore(void)405 create_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 }
420 TEST(create_unnamed_semaphore, "create unnamed semaphore");
421
422 static void
open_named_semaphore(void)423 open_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 }
443 TEST(open_named_semaphore, "create named semaphore");
444
445 static void
getvalue_invalid_semaphore(void)446 getvalue_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 }
460 TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
461
462 static void
post_invalid_semaphore(void)463 post_invalid_semaphore(void)
464 {
465
466 ksem_post_should_fail(STDERR_FILENO, EINVAL);
467 }
468 TEST(post_invalid_semaphore, "post of invalid semaphore");
469
470 static void
wait_invalid_semaphore(void)471 wait_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 }
484 TEST(wait_invalid_semaphore, "wait for invalid semaphore");
485
486 static void
trywait_invalid_semaphore(void)487 trywait_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 }
500 TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
501
502 static void
timedwait_invalid_semaphore(void)503 timedwait_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 }
516 TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
517
518 static int
checkvalue(semid_t id,int expected)519 checkvalue(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
534 static void
post_test(void)535 post_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 }
562 TEST(post_test, "simple post");
563
564 static void
use_after_unlink_test(void)565 use_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 }
615 TEST(use_after_unlink_test, "use named semaphore after unlink");
616
617 static void
unlocked_trywait(void)618 unlocked_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 }
644 TEST(unlocked_trywait, "unlocked trywait");
645
646 static void
locked_trywait(void)647 locked_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 }
678 TEST(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 */
685 static semid_t alarm_id = -1;
686 static int alarm_errno;
687 static int alarm_handler_installed;
688
689 static void
alarm_handler(int signo)690 alarm_handler(int signo)
691 {
692
693 if (ksem_post(alarm_id) < 0)
694 alarm_errno = errno;
695 }
696
697 static int
check_alarm(int just_clear)698 check_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
724 static int
schedule_post(semid_t id,u_int msec)725 schedule_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
751 static int
timedwait(semid_t id,u_int msec,u_int * delta,int error)752 timedwait(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
782 static void
unlocked_timedwait(void)783 unlocked_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 }
814 TEST(unlocked_timedwait, "unlocked timedwait");
815
816 static void
expired_timedwait(void)817 expired_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 }
850 TEST(expired_timedwait, "locked timedwait timeout");
851
852 static void
locked_timedwait(void)853 locked_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 }
896 TEST(locked_timedwait, "locked timedwait");
897
898 static int
testwait(semid_t id,u_int * delta)899 testwait(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
921 static void
unlocked_wait(void)922 unlocked_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 }
953 TEST(unlocked_wait, "unlocked wait");
954
955 static void
locked_wait(void)956 locked_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 }
998 TEST(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 */
1005 static int
wait_twoproc_child(void * arg)1006 wait_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
1019 static void
wait_twoproc_test(void)1020 wait_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 }
1066 TEST(wait_twoproc_test, "two proc wait");
1067
1068 static void
maxvalue_test(void)1069 maxvalue_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 }
1099 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1100
1101 static void
maxvalue_post_test(void)1102 maxvalue_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 }
1115 TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1116
1117 static void
busy_destroy_test(void)1118 busy_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 }
1180 TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1181
1182 static int
exhaust_unnamed_child(void * arg)1183 exhaust_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
1199 static void
exhaust_unnamed_sems(void)1200 exhaust_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 }
1231 TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1232
1233 static int
exhaust_named_child(void * arg)1234 exhaust_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
1253 static void
exhaust_named_sems(void)1254 exhaust_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 }
1292 TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1293
1294 static int
fdlimit_set(void * arg)1295 fdlimit_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
1309 static int
fdlimit_unnamed_child(void * arg)1310 fdlimit_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
1320 static void
fdlimit_unnamed_sems(void)1321 fdlimit_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 }
1351 TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1352
1353 static int
fdlimit_named_child(void * arg)1354 fdlimit_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
1364 static void
fdlimit_named_sems(void)1365 fdlimit_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 }
1402 TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1403
1404 int
main(int argc,char * argv[])1405 main(int argc, char *argv[])
1406 {
1407
1408 signal(SIGSYS, SIG_IGN);
1409 run_tests();
1410 return (0);
1411 }
1412