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