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