1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2008 Yahoo!, Inc.
5 * All rights reserved.
6 * Written by: John Baldwin <[email protected]>
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 */
75 static int
child_worker(int (* func)(void * arg),void * arg,int * stat)76 child_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 */
122 static void
ksem_open_should_fail(const char * path,int flags,mode_t mode,unsigned int value,int error)123 ksem_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 */
144 static void
ksem_unlink_should_fail(const char * path,int error)145 ksem_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 */
163 static void
ksem_close_should_fail(semid_t id,int error)164 ksem_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 */
182 static void
ksem_init_should_fail(unsigned int value,int error)183 ksem_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 */
203 static void
ksem_destroy_should_fail(semid_t id,int error)204 ksem_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 */
222 static void
ksem_post_should_fail(semid_t id,int error)223 ksem_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
237 static void
open_after_unlink(void)238 open_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 }
255 TEST(open_after_unlink, "open after unlink");
256
257 static void
open_invalid_path(void)258 open_invalid_path(void)
259 {
260
261 ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
262 }
263 TEST(open_invalid_path, "open invalid path");
264
265 static void
open_extra_flags(void)266 open_extra_flags(void)
267 {
268
269 ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
270 }
271 TEST(open_extra_flags, "open with extra flags");
272
273 static void
open_bad_value(void)274 open_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 }
281 TEST(open_bad_value, "open with invalid initial value");
282
283 static void
open_bad_path_pointer(void)284 open_bad_path_pointer(void)
285 {
286
287 ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
288 }
289 TEST(open_bad_path_pointer, "open bad path pointer");
290
291 static void
open_path_too_long(void)292 open_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 }
302 TEST(open_path_too_long, "open pathname too long");
303
304 static void
open_nonexisting_semaphore(void)305 open_nonexisting_semaphore(void)
306 {
307
308 ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
309 }
310 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
311
312 static void
exclusive_create_existing_semaphore(void)313 exclusive_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 }
327 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
328
329 static void
init_bad_value(void)330 init_bad_value(void)
331 {
332
333 ksem_init_should_fail(UINT_MAX, EINVAL);
334 }
335 TEST(init_bad_value, "init with invalid initial value");
336
337 static void
unlink_bad_path_pointer(void)338 unlink_bad_path_pointer(void)
339 {
340
341 ksem_unlink_should_fail((char *)1024, EFAULT);
342 }
343 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
344
345 static void
unlink_path_too_long(void)346 unlink_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 }
356 TEST(unlink_path_too_long, "unlink pathname too long");
357
358 static void
destroy_named_semaphore(void)359 destroy_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 }
373 TEST(destroy_named_semaphore, "destroy named semaphore");
374
375 static void
close_unnamed_semaphore(void)376 close_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 }
389 TEST(close_unnamed_semaphore, "close unnamed semaphore");
390
391 static void
destroy_invalid_fd(void)392 destroy_invalid_fd(void)
393 {
394
395 ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
396 }
397 TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
398
399 static void
close_invalid_fd(void)400 close_invalid_fd(void)
401 {
402
403 ksem_close_should_fail(STDERR_FILENO, EINVAL);
404 }
405 TEST(close_invalid_fd, "close non-semaphore file descriptor");
406
407 static void
create_unnamed_semaphore(void)408 create_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 }
423 TEST(create_unnamed_semaphore, "create unnamed semaphore");
424
425 static void
open_named_semaphore(void)426 open_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 }
446 TEST(open_named_semaphore, "create named semaphore");
447
448 static void
getvalue_invalid_semaphore(void)449 getvalue_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 }
463 TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
464
465 static void
post_invalid_semaphore(void)466 post_invalid_semaphore(void)
467 {
468
469 ksem_post_should_fail(STDERR_FILENO, EINVAL);
470 }
471 TEST(post_invalid_semaphore, "post of invalid semaphore");
472
473 static void
wait_invalid_semaphore(void)474 wait_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 }
487 TEST(wait_invalid_semaphore, "wait for invalid semaphore");
488
489 static void
trywait_invalid_semaphore(void)490 trywait_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 }
503 TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
504
505 static void
timedwait_invalid_semaphore(void)506 timedwait_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 }
519 TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
520
521 static int
checkvalue(semid_t id,int expected)522 checkvalue(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
537 static void
post_test(void)538 post_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 }
565 TEST(post_test, "simple post");
566
567 static void
use_after_unlink_test(void)568 use_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 }
618 TEST(use_after_unlink_test, "use named semaphore after unlink");
619
620 static void
unlocked_trywait(void)621 unlocked_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 }
647 TEST(unlocked_trywait, "unlocked trywait");
648
649 static void
locked_trywait(void)650 locked_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 }
681 TEST(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 */
688 static semid_t alarm_id = -1;
689 static int alarm_errno;
690 static int alarm_handler_installed;
691
692 static void
alarm_handler(int signo)693 alarm_handler(int signo)
694 {
695
696 if (ksem_post(alarm_id) < 0)
697 alarm_errno = errno;
698 }
699
700 static int
check_alarm(int just_clear)701 check_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
727 static int
schedule_post(semid_t id,u_int msec)728 schedule_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
754 static int
timedwait(semid_t id,u_int msec,u_int * delta,int error)755 timedwait(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
785 static void
unlocked_timedwait(void)786 unlocked_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 }
817 TEST(unlocked_timedwait, "unlocked timedwait");
818
819 static void
expired_timedwait(void)820 expired_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 }
853 TEST(expired_timedwait, "locked timedwait timeout");
854
855 static void
locked_timedwait(void)856 locked_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 }
899 TEST(locked_timedwait, "locked timedwait");
900
901 static int
testwait(semid_t id,u_int * delta)902 testwait(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
924 static void
unlocked_wait(void)925 unlocked_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 }
956 TEST(unlocked_wait, "unlocked wait");
957
958 static void
locked_wait(void)959 locked_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 }
1001 TEST(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 */
1008 static int
wait_twoproc_child(void * arg)1009 wait_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
1022 static void
wait_twoproc_test(void)1023 wait_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 }
1069 TEST(wait_twoproc_test, "two proc wait");
1070
1071 static void
maxvalue_test(void)1072 maxvalue_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 }
1102 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1103
1104 static void
maxvalue_post_test(void)1105 maxvalue_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 }
1118 TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1119
1120 static void
busy_destroy_test(void)1121 busy_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 }
1183 TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1184
1185 static int
exhaust_unnamed_child(void * arg)1186 exhaust_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
1202 static void
exhaust_unnamed_sems(void)1203 exhaust_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 }
1234 TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1235
1236 static int
exhaust_named_child(void * arg)1237 exhaust_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
1256 static void
exhaust_named_sems(void)1257 exhaust_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 }
1295 TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1296
1297 static int
fdlimit_set(void * arg)1298 fdlimit_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
1312 static int
fdlimit_unnamed_child(void * arg)1313 fdlimit_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
1323 static void
fdlimit_unnamed_sems(void)1324 fdlimit_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 }
1354 TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1355
1356 static int
fdlimit_named_child(void * arg)1357 fdlimit_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
1367 static void
fdlimit_named_sems(void)1368 fdlimit_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 }
1405 TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1406
1407 int
main(int argc,char * argv[])1408 main(int argc, char *argv[])
1409 {
1410
1411 signal(SIGSYS, SIG_IGN);
1412 run_tests();
1413 return (0);
1414 }
1415