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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 690 alarm_handler(int signo) 691 { 692 693 if (ksem_post(alarm_id) < 0) 694 alarm_errno = errno; 695 } 696 697 static int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1405 main(int argc, char *argv[]) 1406 { 1407 1408 signal(SIGSYS, SIG_IGN); 1409 run_tests(); 1410 return (0); 1411 } 1412