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