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