1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2008 Yahoo!, Inc. 5 * All rights reserved. 6 * Written by: John Baldwin <jhb@FreeBSD.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/queue.h> 38 #include <sys/_semaphore.h> 39 #include <sys/sysctl.h> 40 #include <sys/time.h> 41 #include <sys/user.h> 42 #include <sys/wait.h> 43 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <kvm.h> 47 #include <limits.h> 48 #include <semaphore.h> 49 #include <signal.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <time.h> 54 #include <unistd.h> 55 56 #include "test.h" 57 58 #define TEST_PATH "/tmp/posixsem_regression_test" 59 60 #define ELAPSED(elapsed, limit) (abs((elapsed) - (limit)) < 100) 61 62 /* Macros for passing child status to parent over a pipe. */ 63 #define CSTAT(class, error) ((class) << 16 | (error)) 64 #define CSTAT_CLASS(stat) ((stat) >> 16) 65 #define CSTAT_ERROR(stat) ((stat) & 0xffff) 66 67 /* 68 * Helper routine for tests that use a child process. This routine 69 * creates a pipe and forks a child process. The child process runs 70 * the 'func' routine which returns a status integer. The status 71 * integer gets written over the pipe to the parent and returned in 72 * '*stat'. If there is an error in pipe(), fork(), or wait() this 73 * returns -1 and fails the test. 74 */ 75 static int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 693 alarm_handler(int signo) 694 { 695 696 if (ksem_post(alarm_id) < 0) 697 alarm_errno = errno; 698 } 699 700 static int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1408 main(int argc, char *argv[]) 1409 { 1410 1411 signal(SIGSYS, SIG_IGN); 1412 run_tests(); 1413 return (0); 1414 } 1415