1 /*- 2 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 3 * Copyright (c) 2003-2005 SPARTA, Inc. 4 * Copyright (c) 2005 Robert N. M. Watson 5 * All rights reserved. 6 * 7 * This software was developed for the FreeBSD Project in part by Network 8 * Associates Laboratories, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 10 * as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include "opt_mac.h" 38 #include "opt_posix.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/sysproto.h> 43 #include <sys/eventhandler.h> 44 #include <sys/kernel.h> 45 #include <sys/proc.h> 46 #include <sys/lock.h> 47 #include <sys/mutex.h> 48 #include <sys/module.h> 49 #include <sys/condvar.h> 50 #include <sys/sem.h> 51 #include <sys/uio.h> 52 #include <sys/syscall.h> 53 #include <sys/stat.h> 54 #include <sys/sysent.h> 55 #include <sys/sysctl.h> 56 #include <sys/time.h> 57 #include <sys/mac.h> 58 #include <sys/malloc.h> 59 #include <sys/fcntl.h> 60 61 #include <posix4/ksem.h> 62 #include <posix4/posix4.h> 63 #include <posix4/semaphore.h> 64 #include <posix4/_semaphore.h> 65 66 static int sem_count_proc(struct proc *p); 67 static struct ksem *sem_lookup_byname(const char *name); 68 static int sem_create(struct thread *td, const char *name, 69 struct ksem **ksret, mode_t mode, unsigned int value); 70 static void sem_free(struct ksem *ksnew); 71 static int sem_perm(struct thread *td, struct ksem *ks); 72 static void sem_enter(struct proc *p, struct ksem *ks); 73 static int sem_leave(struct proc *p, struct ksem *ks); 74 static void sem_exithook(void *arg, struct proc *p); 75 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2, 76 int flags); 77 static int sem_hasopen(struct thread *td, struct ksem *ks); 78 79 static int kern_sem_close(struct thread *td, semid_t id); 80 static int kern_sem_post(struct thread *td, semid_t id); 81 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag, 82 struct timespec *abstime); 83 static int kern_sem_init(struct thread *td, int dir, unsigned int value, 84 semid_t *idp); 85 static int kern_sem_open(struct thread *td, int dir, const char *name, 86 int oflag, mode_t mode, unsigned int value, semid_t *idp); 87 static int kern_sem_unlink(struct thread *td, const char *name); 88 89 #ifndef SEM_MAX 90 #define SEM_MAX 30 91 #endif 92 93 #define SEM_MAX_NAMELEN 14 94 95 #define SEM_TO_ID(x) ((intptr_t)(x)) 96 #define ID_TO_SEM(x) id_to_sem(x) 97 98 /* 99 * available semaphores go here, this includes sem_init and any semaphores 100 * created via sem_open that have not yet been unlinked. 101 */ 102 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head); 103 /* 104 * semaphores still in use but have been sem_unlink()'d go here. 105 */ 106 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead); 107 108 static struct mtx sem_lock; 109 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data"); 110 111 static int nsems = 0; 112 SYSCTL_DECL(_p1003_1b); 113 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, ""); 114 115 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag; 116 117 #ifdef SEM_DEBUG 118 #define DP(x) printf x 119 #else 120 #define DP(x) 121 #endif 122 123 static __inline 124 void 125 sem_ref(struct ksem *ks) 126 { 127 128 mtx_assert(&sem_lock, MA_OWNED); 129 ks->ks_ref++; 130 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref)); 131 } 132 133 static __inline 134 void 135 sem_rel(struct ksem *ks) 136 { 137 138 mtx_assert(&sem_lock, MA_OWNED); 139 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1)); 140 if (--ks->ks_ref == 0) 141 sem_free(ks); 142 } 143 144 static __inline struct ksem *id_to_sem(semid_t id); 145 146 static __inline 147 struct ksem * 148 id_to_sem(id) 149 semid_t id; 150 { 151 struct ksem *ks; 152 153 mtx_assert(&sem_lock, MA_OWNED); 154 DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id)); 155 LIST_FOREACH(ks, &ksem_head, ks_entry) { 156 DP(("id_to_sem: ks = %p\n", ks)); 157 if (ks == (struct ksem *)id) 158 return (ks); 159 } 160 return (NULL); 161 } 162 163 static struct ksem * 164 sem_lookup_byname(name) 165 const char *name; 166 { 167 struct ksem *ks; 168 169 mtx_assert(&sem_lock, MA_OWNED); 170 LIST_FOREACH(ks, &ksem_head, ks_entry) 171 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0) 172 return (ks); 173 return (NULL); 174 } 175 176 static int 177 sem_create(td, name, ksret, mode, value) 178 struct thread *td; 179 const char *name; 180 struct ksem **ksret; 181 mode_t mode; 182 unsigned int value; 183 { 184 struct ksem *ret; 185 struct proc *p; 186 struct ucred *uc; 187 size_t len; 188 int error; 189 190 DP(("sem_create\n")); 191 p = td->td_proc; 192 uc = td->td_ucred; 193 if (value > SEM_VALUE_MAX) 194 return (EINVAL); 195 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO); 196 if (name != NULL) { 197 len = strlen(name); 198 if (len > SEM_MAX_NAMELEN) { 199 free(ret, M_SEM); 200 return (ENAMETOOLONG); 201 } 202 /* name must start with a '/' but not contain one. */ 203 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) { 204 free(ret, M_SEM); 205 return (EINVAL); 206 } 207 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK); 208 strcpy(ret->ks_name, name); 209 } else { 210 ret->ks_name = NULL; 211 } 212 ret->ks_mode = mode; 213 ret->ks_value = value; 214 ret->ks_ref = 1; 215 ret->ks_waiters = 0; 216 ret->ks_uid = uc->cr_uid; 217 ret->ks_gid = uc->cr_gid; 218 ret->ks_onlist = 0; 219 cv_init(&ret->ks_cv, "sem"); 220 LIST_INIT(&ret->ks_users); 221 #ifdef MAC 222 mac_init_posix_sem(ret); 223 mac_create_posix_sem(uc, ret); 224 #endif 225 if (name != NULL) 226 sem_enter(td->td_proc, ret); 227 *ksret = ret; 228 mtx_lock(&sem_lock); 229 if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) { 230 sem_leave(td->td_proc, ret); 231 sem_free(ret); 232 error = ENFILE; 233 } else { 234 nsems++; 235 error = 0; 236 } 237 mtx_unlock(&sem_lock); 238 return (error); 239 } 240 241 #ifndef _SYS_SYSPROTO_H_ 242 struct ksem_init_args { 243 unsigned int value; 244 semid_t *idp; 245 }; 246 int ksem_init(struct thread *td, struct ksem_init_args *uap); 247 #endif 248 int 249 ksem_init(td, uap) 250 struct thread *td; 251 struct ksem_init_args *uap; 252 { 253 int error; 254 255 error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp); 256 return (error); 257 } 258 259 static int 260 kern_sem_init(td, dir, value, idp) 261 struct thread *td; 262 int dir; 263 unsigned int value; 264 semid_t *idp; 265 { 266 struct ksem *ks; 267 semid_t id; 268 int error; 269 270 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value); 271 if (error) 272 return (error); 273 id = SEM_TO_ID(ks); 274 if (dir == UIO_USERSPACE) { 275 error = copyout(&id, idp, sizeof(id)); 276 if (error) { 277 mtx_lock(&sem_lock); 278 sem_rel(ks); 279 mtx_unlock(&sem_lock); 280 return (error); 281 } 282 } else { 283 *idp = id; 284 } 285 mtx_lock(&sem_lock); 286 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); 287 ks->ks_onlist = 1; 288 mtx_unlock(&sem_lock); 289 return (error); 290 } 291 292 #ifndef _SYS_SYSPROTO_H_ 293 struct ksem_open_args { 294 char *name; 295 int oflag; 296 mode_t mode; 297 unsigned int value; 298 semid_t *idp; 299 }; 300 int ksem_open(struct thread *td, struct ksem_open_args *uap); 301 #endif 302 int 303 ksem_open(td, uap) 304 struct thread *td; 305 struct ksem_open_args *uap; 306 { 307 char name[SEM_MAX_NAMELEN + 1]; 308 size_t done; 309 int error; 310 311 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done); 312 if (error) 313 return (error); 314 DP((">>> sem_open start\n")); 315 error = kern_sem_open(td, UIO_USERSPACE, 316 name, uap->oflag, uap->mode, uap->value, uap->idp); 317 DP(("<<< sem_open end\n")); 318 return (error); 319 } 320 321 static int 322 kern_sem_open(td, dir, name, oflag, mode, value, idp) 323 struct thread *td; 324 int dir; 325 const char *name; 326 int oflag; 327 mode_t mode; 328 unsigned int value; 329 semid_t *idp; 330 { 331 struct ksem *ksnew, *ks; 332 int error; 333 semid_t id; 334 335 ksnew = NULL; 336 mtx_lock(&sem_lock); 337 ks = sem_lookup_byname(name); 338 /* 339 * If we found it but O_EXCL is set, error. 340 */ 341 if (ks != NULL && (oflag & O_EXCL) != 0) { 342 mtx_unlock(&sem_lock); 343 return (EEXIST); 344 } 345 /* 346 * If we didn't find it... 347 */ 348 if (ks == NULL) { 349 /* 350 * didn't ask for creation? error. 351 */ 352 if ((oflag & O_CREAT) == 0) { 353 mtx_unlock(&sem_lock); 354 return (ENOENT); 355 } 356 /* 357 * We may block during creation, so drop the lock. 358 */ 359 mtx_unlock(&sem_lock); 360 error = sem_create(td, name, &ksnew, mode, value); 361 if (error != 0) 362 return (error); 363 id = SEM_TO_ID(ksnew); 364 if (dir == UIO_USERSPACE) { 365 DP(("about to copyout! %d to %p\n", id, idp)); 366 error = copyout(&id, idp, sizeof(id)); 367 if (error) { 368 mtx_lock(&sem_lock); 369 sem_leave(td->td_proc, ksnew); 370 sem_rel(ksnew); 371 mtx_unlock(&sem_lock); 372 return (error); 373 } 374 } else { 375 DP(("about to set! %d to %p\n", id, idp)); 376 *idp = id; 377 } 378 /* 379 * We need to make sure we haven't lost a race while 380 * allocating during creation. 381 */ 382 mtx_lock(&sem_lock); 383 ks = sem_lookup_byname(name); 384 if (ks != NULL) { 385 /* we lost... */ 386 sem_leave(td->td_proc, ksnew); 387 sem_rel(ksnew); 388 /* we lost and we can't loose... */ 389 if ((oflag & O_EXCL) != 0) { 390 mtx_unlock(&sem_lock); 391 return (EEXIST); 392 } 393 } else { 394 DP(("sem_create: about to add to list...\n")); 395 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 396 DP(("sem_create: setting list bit...\n")); 397 ksnew->ks_onlist = 1; 398 DP(("sem_create: done, about to unlock...\n")); 399 } 400 } else { 401 #ifdef MAC 402 error = mac_check_posix_sem_open(td->td_ucred, ks); 403 if (error) 404 goto err_open; 405 #endif 406 /* 407 * if we aren't the creator, then enforce permissions. 408 */ 409 error = sem_perm(td, ks); 410 if (error) 411 goto err_open; 412 sem_ref(ks); 413 mtx_unlock(&sem_lock); 414 id = SEM_TO_ID(ks); 415 if (dir == UIO_USERSPACE) { 416 error = copyout(&id, idp, sizeof(id)); 417 if (error) { 418 mtx_lock(&sem_lock); 419 sem_rel(ks); 420 mtx_unlock(&sem_lock); 421 return (error); 422 } 423 } else { 424 *idp = id; 425 } 426 sem_enter(td->td_proc, ks); 427 mtx_lock(&sem_lock); 428 sem_rel(ks); 429 } 430 err_open: 431 mtx_unlock(&sem_lock); 432 return (error); 433 } 434 435 static int 436 sem_perm(td, ks) 437 struct thread *td; 438 struct ksem *ks; 439 { 440 struct ucred *uc; 441 442 uc = td->td_ucred; 443 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n", 444 uc->cr_uid, uc->cr_gid, 445 ks->ks_uid, ks->ks_gid, ks->ks_mode)); 446 if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) || 447 (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) || 448 (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0) 449 return (0); 450 return (EPERM); 451 } 452 453 static void 454 sem_free(struct ksem *ks) 455 { 456 457 nsems--; 458 if (ks->ks_onlist) 459 LIST_REMOVE(ks, ks_entry); 460 if (ks->ks_name != NULL) 461 free(ks->ks_name, M_SEM); 462 cv_destroy(&ks->ks_cv); 463 free(ks, M_SEM); 464 } 465 466 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks); 467 468 static __inline struct kuser * 469 sem_getuser(p, ks) 470 struct proc *p; 471 struct ksem *ks; 472 { 473 struct kuser *k; 474 475 LIST_FOREACH(k, &ks->ks_users, ku_next) 476 if (k->ku_pid == p->p_pid) 477 return (k); 478 return (NULL); 479 } 480 481 static int 482 sem_hasopen(td, ks) 483 struct thread *td; 484 struct ksem *ks; 485 { 486 487 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0) 488 || sem_getuser(td->td_proc, ks) != NULL); 489 } 490 491 static int 492 sem_leave(p, ks) 493 struct proc *p; 494 struct ksem *ks; 495 { 496 struct kuser *k; 497 498 DP(("sem_leave: ks = %p\n", ks)); 499 k = sem_getuser(p, ks); 500 DP(("sem_leave: ks = %p, k = %p\n", ks, k)); 501 if (k != NULL) { 502 LIST_REMOVE(k, ku_next); 503 sem_rel(ks); 504 DP(("sem_leave: about to free k\n")); 505 free(k, M_SEM); 506 DP(("sem_leave: returning\n")); 507 return (0); 508 } 509 return (EINVAL); 510 } 511 512 static void 513 sem_enter(p, ks) 514 struct proc *p; 515 struct ksem *ks; 516 { 517 struct kuser *ku, *k; 518 519 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK); 520 ku->ku_pid = p->p_pid; 521 mtx_lock(&sem_lock); 522 k = sem_getuser(p, ks); 523 if (k != NULL) { 524 mtx_unlock(&sem_lock); 525 free(ku, M_TEMP); 526 return; 527 } 528 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next); 529 sem_ref(ks); 530 mtx_unlock(&sem_lock); 531 } 532 533 #ifndef _SYS_SYSPROTO_H_ 534 struct ksem_unlink_args { 535 char *name; 536 }; 537 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap); 538 #endif 539 540 int 541 ksem_unlink(td, uap) 542 struct thread *td; 543 struct ksem_unlink_args *uap; 544 { 545 char name[SEM_MAX_NAMELEN + 1]; 546 size_t done; 547 int error; 548 549 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done); 550 return (error ? error : 551 kern_sem_unlink(td, name)); 552 } 553 554 static int 555 kern_sem_unlink(td, name) 556 struct thread *td; 557 const char *name; 558 { 559 struct ksem *ks; 560 int error; 561 562 mtx_lock(&sem_lock); 563 ks = sem_lookup_byname(name); 564 if (ks != NULL) { 565 #ifdef MAC 566 error = mac_check_posix_sem_unlink(td->td_ucred, ks); 567 if (error) { 568 mtx_unlock(&sem_lock); 569 return (error); 570 } 571 #endif 572 error = sem_perm(td, ks); 573 } else 574 error = ENOENT; 575 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error)); 576 if (error == 0) { 577 LIST_REMOVE(ks, ks_entry); 578 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 579 sem_rel(ks); 580 } 581 mtx_unlock(&sem_lock); 582 return (error); 583 } 584 585 #ifndef _SYS_SYSPROTO_H_ 586 struct ksem_close_args { 587 semid_t id; 588 }; 589 int ksem_close(struct thread *td, struct ksem_close_args *uap); 590 #endif 591 592 int 593 ksem_close(struct thread *td, struct ksem_close_args *uap) 594 { 595 596 return (kern_sem_close(td, uap->id)); 597 } 598 599 static int 600 kern_sem_close(td, id) 601 struct thread *td; 602 semid_t id; 603 { 604 struct ksem *ks; 605 int error; 606 607 error = EINVAL; 608 mtx_lock(&sem_lock); 609 ks = ID_TO_SEM(id); 610 /* this is not a valid operation for unnamed sems */ 611 if (ks != NULL && ks->ks_name != NULL) 612 error = sem_leave(td->td_proc, ks); 613 mtx_unlock(&sem_lock); 614 return (error); 615 } 616 617 #ifndef _SYS_SYSPROTO_H_ 618 struct ksem_post_args { 619 semid_t id; 620 }; 621 int ksem_post(struct thread *td, struct ksem_post_args *uap); 622 #endif 623 int 624 ksem_post(td, uap) 625 struct thread *td; 626 struct ksem_post_args *uap; 627 { 628 629 return (kern_sem_post(td, uap->id)); 630 } 631 632 static int 633 kern_sem_post(td, id) 634 struct thread *td; 635 semid_t id; 636 { 637 struct ksem *ks; 638 int error; 639 640 mtx_lock(&sem_lock); 641 ks = ID_TO_SEM(id); 642 if (ks == NULL || !sem_hasopen(td, ks)) { 643 error = EINVAL; 644 goto err; 645 } 646 #ifdef MAC 647 error = mac_check_posix_sem_post(td->td_ucred, ks); 648 if (error) 649 goto err; 650 #endif 651 if (ks->ks_value == SEM_VALUE_MAX) { 652 error = EOVERFLOW; 653 goto err; 654 } 655 ++ks->ks_value; 656 if (ks->ks_waiters > 0) 657 cv_signal(&ks->ks_cv); 658 error = 0; 659 err: 660 mtx_unlock(&sem_lock); 661 return (error); 662 } 663 664 #ifndef _SYS_SYSPROTO_H_ 665 struct ksem_wait_args { 666 semid_t id; 667 }; 668 int ksem_wait(struct thread *td, struct ksem_wait_args *uap); 669 #endif 670 671 int 672 ksem_wait(td, uap) 673 struct thread *td; 674 struct ksem_wait_args *uap; 675 { 676 677 return (kern_sem_wait(td, uap->id, 0, NULL)); 678 } 679 680 #ifndef _SYS_SYSPROTO_H_ 681 struct ksem_timedwait_args { 682 semid_t id; 683 const struct timespec *abstime; 684 }; 685 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap); 686 #endif 687 int 688 ksem_timedwait(td, uap) 689 struct thread *td; 690 struct ksem_timedwait_args *uap; 691 { 692 struct timespec abstime; 693 struct timespec *ts; 694 int error; 695 696 /* We allow a null timespec (wait forever). */ 697 if (uap->abstime == NULL) 698 ts = NULL; 699 else { 700 error = copyin(uap->abstime, &abstime, sizeof(abstime)); 701 if (error != 0) 702 return (error); 703 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0) 704 return (EINVAL); 705 ts = &abstime; 706 } 707 return (kern_sem_wait(td, uap->id, 0, ts)); 708 } 709 710 #ifndef _SYS_SYSPROTO_H_ 711 struct ksem_trywait_args { 712 semid_t id; 713 }; 714 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap); 715 #endif 716 int 717 ksem_trywait(td, uap) 718 struct thread *td; 719 struct ksem_trywait_args *uap; 720 { 721 722 return (kern_sem_wait(td, uap->id, 1, NULL)); 723 } 724 725 static int 726 kern_sem_wait(td, id, tryflag, abstime) 727 struct thread *td; 728 semid_t id; 729 int tryflag; 730 struct timespec *abstime; 731 { 732 struct timespec ts1, ts2; 733 struct timeval tv; 734 struct ksem *ks; 735 int error; 736 737 DP((">>> kern_sem_wait entered!\n")); 738 mtx_lock(&sem_lock); 739 ks = ID_TO_SEM(id); 740 if (ks == NULL) { 741 DP(("kern_sem_wait ks == NULL\n")); 742 error = EINVAL; 743 goto err; 744 } 745 sem_ref(ks); 746 if (!sem_hasopen(td, ks)) { 747 DP(("kern_sem_wait hasopen failed\n")); 748 error = EINVAL; 749 goto err; 750 } 751 #ifdef MAC 752 error = mac_check_posix_sem_wait(td->td_ucred, ks); 753 if (error) { 754 DP(("kern_sem_wait mac failed\n")); 755 goto err; 756 } 757 #endif 758 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag)); 759 if (ks->ks_value == 0) { 760 ks->ks_waiters++; 761 if (tryflag != 0) 762 error = EAGAIN; 763 else if (abstime == NULL) 764 error = cv_wait_sig(&ks->ks_cv, &sem_lock); 765 else { 766 for (;;) { 767 ts1 = *abstime; 768 getnanotime(&ts2); 769 timespecsub(&ts1, &ts2); 770 TIMESPEC_TO_TIMEVAL(&tv, &ts1); 771 if (tv.tv_sec < 0) { 772 error = ETIMEDOUT; 773 break; 774 } 775 error = cv_timedwait_sig(&ks->ks_cv, 776 &sem_lock, tvtohz(&tv)); 777 if (error != EWOULDBLOCK) 778 break; 779 } 780 } 781 ks->ks_waiters--; 782 if (error) 783 goto err; 784 } 785 ks->ks_value--; 786 error = 0; 787 err: 788 if (ks != NULL) 789 sem_rel(ks); 790 mtx_unlock(&sem_lock); 791 DP(("<<< kern_sem_wait leaving, error = %d\n", error)); 792 return (error); 793 } 794 795 #ifndef _SYS_SYSPROTO_H_ 796 struct ksem_getvalue_args { 797 semid_t id; 798 int *val; 799 }; 800 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap); 801 #endif 802 int 803 ksem_getvalue(td, uap) 804 struct thread *td; 805 struct ksem_getvalue_args *uap; 806 { 807 struct ksem *ks; 808 int error, val; 809 810 mtx_lock(&sem_lock); 811 ks = ID_TO_SEM(uap->id); 812 if (ks == NULL || !sem_hasopen(td, ks)) { 813 mtx_unlock(&sem_lock); 814 return (EINVAL); 815 } 816 #ifdef MAC 817 error = mac_check_posix_sem_getvalue(td->td_ucred, ks); 818 if (error) { 819 mtx_unlock(&sem_lock); 820 return (error); 821 } 822 #endif 823 val = ks->ks_value; 824 mtx_unlock(&sem_lock); 825 error = copyout(&val, uap->val, sizeof(val)); 826 return (error); 827 } 828 829 #ifndef _SYS_SYSPROTO_H_ 830 struct ksem_destroy_args { 831 semid_t id; 832 }; 833 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap); 834 #endif 835 int 836 ksem_destroy(td, uap) 837 struct thread *td; 838 struct ksem_destroy_args *uap; 839 { 840 struct ksem *ks; 841 int error; 842 843 mtx_lock(&sem_lock); 844 ks = ID_TO_SEM(uap->id); 845 if (ks == NULL || !sem_hasopen(td, ks) || 846 ks->ks_name != NULL) { 847 error = EINVAL; 848 goto err; 849 } 850 #ifdef MAC 851 error = mac_check_posix_sem_destroy(td->td_ucred, ks); 852 if (error) 853 goto err; 854 #endif 855 if (ks->ks_waiters != 0) { 856 error = EBUSY; 857 goto err; 858 } 859 sem_rel(ks); 860 error = 0; 861 err: 862 mtx_unlock(&sem_lock); 863 return (error); 864 } 865 866 /* 867 * Count the number of kusers associated with a proc, so as to guess at how 868 * many to allocate when forking. 869 */ 870 static int 871 sem_count_proc(p) 872 struct proc *p; 873 { 874 struct ksem *ks; 875 struct kuser *ku; 876 int count; 877 878 mtx_assert(&sem_lock, MA_OWNED); 879 880 count = 0; 881 LIST_FOREACH(ks, &ksem_head, ks_entry) { 882 LIST_FOREACH(ku, &ks->ks_users, ku_next) { 883 if (ku->ku_pid == p->p_pid) 884 count++; 885 } 886 } 887 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) { 888 LIST_FOREACH(ku, &ks->ks_users, ku_next) { 889 if (ku->ku_pid == p->p_pid) 890 count++; 891 } 892 } 893 return (count); 894 } 895 896 /* 897 * When a process forks, the child process must gain a reference to each open 898 * semaphore in the parent process, whether it is unlinked or not. This 899 * requires allocating a kuser structure for each semaphore reference in the 900 * new process. Because the set of semaphores in the parent can change while 901 * the fork is in progress, we have to handle races -- first we attempt to 902 * allocate enough storage to acquire references to each of the semaphores, 903 * then we enter the semaphores and release the temporary references. 904 */ 905 static void 906 sem_forkhook(arg, p1, p2, flags) 907 void *arg; 908 struct proc *p1; 909 struct proc *p2; 910 int flags; 911 { 912 struct ksem *ks, **sem_array; 913 int count, i, new_count; 914 struct kuser *ku; 915 916 mtx_lock(&sem_lock); 917 count = sem_count_proc(p1); 918 if (count == 0) { 919 mtx_unlock(&sem_lock); 920 return; 921 } 922 race_lost: 923 mtx_assert(&sem_lock, MA_OWNED); 924 mtx_unlock(&sem_lock); 925 sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK); 926 mtx_lock(&sem_lock); 927 new_count = sem_count_proc(p1); 928 if (count < new_count) { 929 /* Lost race, repeat and allocate more storage. */ 930 free(sem_array, M_TEMP); 931 count = new_count; 932 goto race_lost; 933 } 934 /* 935 * Given an array capable of storing an adequate number of semaphore 936 * references, now walk the list of semaphores and acquire a new 937 * reference for any semaphore opened by p1. 938 */ 939 count = new_count; 940 i = 0; 941 LIST_FOREACH(ks, &ksem_head, ks_entry) { 942 LIST_FOREACH(ku, &ks->ks_users, ku_next) { 943 if (ku->ku_pid == p1->p_pid) { 944 sem_ref(ks); 945 sem_array[i] = ks; 946 i++; 947 break; 948 } 949 } 950 } 951 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) { 952 LIST_FOREACH(ku, &ks->ks_users, ku_next) { 953 if (ku->ku_pid == p1->p_pid) { 954 sem_ref(ks); 955 sem_array[i] = ks; 956 i++; 957 break; 958 } 959 } 960 } 961 mtx_unlock(&sem_lock); 962 KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count)); 963 /* 964 * Now cause p2 to enter each of the referenced semaphores, then 965 * release our temporary reference. This is pretty inefficient. 966 * Finally, free our temporary array. 967 */ 968 for (i = 0; i < count; i++) { 969 sem_enter(p2, sem_array[i]); 970 mtx_lock(&sem_lock); 971 sem_rel(sem_array[i]); 972 mtx_unlock(&sem_lock); 973 } 974 free(sem_array, M_TEMP); 975 } 976 977 static void 978 sem_exithook(arg, p) 979 void *arg; 980 struct proc *p; 981 { 982 struct ksem *ks, *ksnext; 983 984 mtx_lock(&sem_lock); 985 ks = LIST_FIRST(&ksem_head); 986 while (ks != NULL) { 987 ksnext = LIST_NEXT(ks, ks_entry); 988 sem_leave(p, ks); 989 ks = ksnext; 990 } 991 ks = LIST_FIRST(&ksem_deadhead); 992 while (ks != NULL) { 993 ksnext = LIST_NEXT(ks, ks_entry); 994 sem_leave(p, ks); 995 ks = ksnext; 996 } 997 mtx_unlock(&sem_lock); 998 } 999 1000 static int 1001 sem_modload(struct module *module, int cmd, void *arg) 1002 { 1003 int error = 0; 1004 1005 switch (cmd) { 1006 case MOD_LOAD: 1007 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF); 1008 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX); 1009 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX); 1010 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook, 1011 NULL, EVENTHANDLER_PRI_ANY); 1012 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook, 1013 NULL, EVENTHANDLER_PRI_ANY); 1014 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY); 1015 break; 1016 case MOD_UNLOAD: 1017 if (nsems != 0) { 1018 error = EOPNOTSUPP; 1019 break; 1020 } 1021 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag); 1022 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag); 1023 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag); 1024 mtx_destroy(&sem_lock); 1025 break; 1026 case MOD_SHUTDOWN: 1027 break; 1028 default: 1029 error = EINVAL; 1030 break; 1031 } 1032 return (error); 1033 } 1034 1035 static moduledata_t sem_mod = { 1036 "sem", 1037 &sem_modload, 1038 NULL 1039 }; 1040 1041 SYSCALL_MODULE_HELPER(ksem_init); 1042 SYSCALL_MODULE_HELPER(ksem_open); 1043 SYSCALL_MODULE_HELPER(ksem_unlink); 1044 SYSCALL_MODULE_HELPER(ksem_close); 1045 SYSCALL_MODULE_HELPER(ksem_post); 1046 SYSCALL_MODULE_HELPER(ksem_wait); 1047 SYSCALL_MODULE_HELPER(ksem_timedwait); 1048 SYSCALL_MODULE_HELPER(ksem_trywait); 1049 SYSCALL_MODULE_HELPER(ksem_getvalue); 1050 SYSCALL_MODULE_HELPER(ksem_destroy); 1051 1052 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 1053 MODULE_VERSION(sem, 1); 1054