1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 5 * Copyright (c) 2003-2005 SPARTA, Inc. 6 * Copyright (c) 2005, 2016-2017 Robert N. M. Watson 7 * All rights reserved. 8 * 9 * This software was developed for the FreeBSD Project in part by Network 10 * Associates Laboratories, the Security Research Division of Network 11 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 12 * as part of the DARPA CHATS research program. 13 * 14 * Portions of this software were developed by BAE Systems, the University of 15 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL 16 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent 17 * Computing (TC) research program. 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions 21 * are met: 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include "opt_compat.h" 45 #include "opt_posix.h" 46 47 #include <sys/param.h> 48 #include <sys/capsicum.h> 49 #include <sys/condvar.h> 50 #include <sys/fcntl.h> 51 #include <sys/file.h> 52 #include <sys/filedesc.h> 53 #include <sys/fnv_hash.h> 54 #include <sys/jail.h> 55 #include <sys/kernel.h> 56 #include <sys/ksem.h> 57 #include <sys/lock.h> 58 #include <sys/malloc.h> 59 #include <sys/module.h> 60 #include <sys/mutex.h> 61 #include <sys/priv.h> 62 #include <sys/proc.h> 63 #include <sys/posix4.h> 64 #include <sys/_semaphore.h> 65 #include <sys/stat.h> 66 #include <sys/syscall.h> 67 #include <sys/syscallsubr.h> 68 #include <sys/sysctl.h> 69 #include <sys/sysent.h> 70 #include <sys/sysproto.h> 71 #include <sys/systm.h> 72 #include <sys/sx.h> 73 #include <sys/user.h> 74 #include <sys/vnode.h> 75 76 #include <security/audit/audit.h> 77 #include <security/mac/mac_framework.h> 78 79 FEATURE(p1003_1b_semaphores, "POSIX P1003.1B semaphores support"); 80 /* 81 * TODO 82 * 83 * - Resource limits? 84 * - Replace global sem_lock with mtx_pool locks? 85 * - Add a MAC check_create() hook for creating new named semaphores. 86 */ 87 88 #ifndef SEM_MAX 89 #define SEM_MAX 30 90 #endif 91 92 #ifdef SEM_DEBUG 93 #define DP(x) printf x 94 #else 95 #define DP(x) 96 #endif 97 98 struct ksem_mapping { 99 char *km_path; 100 Fnv32_t km_fnv; 101 struct ksem *km_ksem; 102 LIST_ENTRY(ksem_mapping) km_link; 103 }; 104 105 static MALLOC_DEFINE(M_KSEM, "ksem", "semaphore file descriptor"); 106 static LIST_HEAD(, ksem_mapping) *ksem_dictionary; 107 static struct sx ksem_dict_lock; 108 static struct mtx ksem_count_lock; 109 static struct mtx sem_lock; 110 static u_long ksem_hash; 111 static int ksem_dead; 112 113 #define KSEM_HASH(fnv) (&ksem_dictionary[(fnv) & ksem_hash]) 114 115 static int nsems = 0; 116 SYSCTL_DECL(_p1003_1b); 117 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, 118 "Number of active kernel POSIX semaphores"); 119 120 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag, 121 struct timespec *abstime); 122 static int ksem_access(struct ksem *ks, struct ucred *ucred); 123 static struct ksem *ksem_alloc(struct ucred *ucred, mode_t mode, 124 unsigned int value); 125 static int ksem_create(struct thread *td, const char *path, 126 semid_t *semidp, mode_t mode, unsigned int value, 127 int flags, int compat32); 128 static void ksem_drop(struct ksem *ks); 129 static int ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, 130 struct file **fpp); 131 static struct ksem *ksem_hold(struct ksem *ks); 132 static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks); 133 static struct ksem *ksem_lookup(char *path, Fnv32_t fnv); 134 static void ksem_module_destroy(void); 135 static int ksem_module_init(void); 136 static int ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred); 137 static int sem_modload(struct module *module, int cmd, void *arg); 138 139 static fo_stat_t ksem_stat; 140 static fo_close_t ksem_closef; 141 static fo_chmod_t ksem_chmod; 142 static fo_chown_t ksem_chown; 143 static fo_fill_kinfo_t ksem_fill_kinfo; 144 145 /* File descriptor operations. */ 146 static struct fileops ksem_ops = { 147 .fo_read = invfo_rdwr, 148 .fo_write = invfo_rdwr, 149 .fo_truncate = invfo_truncate, 150 .fo_ioctl = invfo_ioctl, 151 .fo_poll = invfo_poll, 152 .fo_kqfilter = invfo_kqfilter, 153 .fo_stat = ksem_stat, 154 .fo_close = ksem_closef, 155 .fo_chmod = ksem_chmod, 156 .fo_chown = ksem_chown, 157 .fo_sendfile = invfo_sendfile, 158 .fo_fill_kinfo = ksem_fill_kinfo, 159 .fo_flags = DFLAG_PASSABLE 160 }; 161 162 FEATURE(posix_sem, "POSIX semaphores"); 163 164 static int 165 ksem_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, 166 struct thread *td) 167 { 168 struct ksem *ks; 169 #ifdef MAC 170 int error; 171 #endif 172 173 ks = fp->f_data; 174 175 #ifdef MAC 176 error = mac_posixsem_check_stat(active_cred, fp->f_cred, ks); 177 if (error) 178 return (error); 179 #endif 180 181 /* 182 * Attempt to return sanish values for fstat() on a semaphore 183 * file descriptor. 184 */ 185 bzero(sb, sizeof(*sb)); 186 187 mtx_lock(&sem_lock); 188 sb->st_atim = ks->ks_atime; 189 sb->st_ctim = ks->ks_ctime; 190 sb->st_mtim = ks->ks_mtime; 191 sb->st_birthtim = ks->ks_birthtime; 192 sb->st_uid = ks->ks_uid; 193 sb->st_gid = ks->ks_gid; 194 sb->st_mode = S_IFREG | ks->ks_mode; /* XXX */ 195 mtx_unlock(&sem_lock); 196 197 return (0); 198 } 199 200 static int 201 ksem_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, 202 struct thread *td) 203 { 204 struct ksem *ks; 205 int error; 206 207 error = 0; 208 ks = fp->f_data; 209 mtx_lock(&sem_lock); 210 #ifdef MAC 211 error = mac_posixsem_check_setmode(active_cred, ks, mode); 212 if (error != 0) 213 goto out; 214 #endif 215 error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid, VADMIN, 216 active_cred, NULL); 217 if (error != 0) 218 goto out; 219 ks->ks_mode = mode & ACCESSPERMS; 220 out: 221 mtx_unlock(&sem_lock); 222 return (error); 223 } 224 225 static int 226 ksem_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, 227 struct thread *td) 228 { 229 struct ksem *ks; 230 int error; 231 232 error = 0; 233 ks = fp->f_data; 234 mtx_lock(&sem_lock); 235 #ifdef MAC 236 error = mac_posixsem_check_setowner(active_cred, ks, uid, gid); 237 if (error != 0) 238 goto out; 239 #endif 240 if (uid == (uid_t)-1) 241 uid = ks->ks_uid; 242 if (gid == (gid_t)-1) 243 gid = ks->ks_gid; 244 if (((uid != ks->ks_uid && uid != active_cred->cr_uid) || 245 (gid != ks->ks_gid && !groupmember(gid, active_cred))) && 246 (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0))) 247 goto out; 248 ks->ks_uid = uid; 249 ks->ks_gid = gid; 250 out: 251 mtx_unlock(&sem_lock); 252 return (error); 253 } 254 255 static int 256 ksem_closef(struct file *fp, struct thread *td) 257 { 258 struct ksem *ks; 259 260 ks = fp->f_data; 261 fp->f_data = NULL; 262 ksem_drop(ks); 263 264 return (0); 265 } 266 267 static int 268 ksem_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) 269 { 270 const char *path, *pr_path; 271 struct ksem *ks; 272 size_t pr_pathlen; 273 274 kif->kf_type = KF_TYPE_SEM; 275 ks = fp->f_data; 276 mtx_lock(&sem_lock); 277 kif->kf_un.kf_sem.kf_sem_value = ks->ks_value; 278 kif->kf_un.kf_sem.kf_sem_mode = S_IFREG | ks->ks_mode; /* XXX */ 279 mtx_unlock(&sem_lock); 280 if (ks->ks_path != NULL) { 281 sx_slock(&ksem_dict_lock); 282 if (ks->ks_path != NULL) { 283 path = ks->ks_path; 284 pr_path = curthread->td_ucred->cr_prison->pr_path; 285 if (strcmp(pr_path, "/") != 0) { 286 /* Return the jail-rooted pathname. */ 287 pr_pathlen = strlen(pr_path); 288 if (strncmp(path, pr_path, pr_pathlen) == 0 && 289 path[pr_pathlen] == '/') 290 path += pr_pathlen; 291 } 292 strlcpy(kif->kf_path, path, sizeof(kif->kf_path)); 293 } 294 sx_sunlock(&ksem_dict_lock); 295 } 296 return (0); 297 } 298 299 /* 300 * ksem object management including creation and reference counting 301 * routines. 302 */ 303 static struct ksem * 304 ksem_alloc(struct ucred *ucred, mode_t mode, unsigned int value) 305 { 306 struct ksem *ks; 307 308 mtx_lock(&ksem_count_lock); 309 if (nsems == p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX) || ksem_dead) { 310 mtx_unlock(&ksem_count_lock); 311 return (NULL); 312 } 313 nsems++; 314 mtx_unlock(&ksem_count_lock); 315 ks = malloc(sizeof(*ks), M_KSEM, M_WAITOK | M_ZERO); 316 ks->ks_uid = ucred->cr_uid; 317 ks->ks_gid = ucred->cr_gid; 318 ks->ks_mode = mode; 319 ks->ks_value = value; 320 cv_init(&ks->ks_cv, "ksem"); 321 vfs_timestamp(&ks->ks_birthtime); 322 ks->ks_atime = ks->ks_mtime = ks->ks_ctime = ks->ks_birthtime; 323 refcount_init(&ks->ks_ref, 1); 324 #ifdef MAC 325 mac_posixsem_init(ks); 326 mac_posixsem_create(ucred, ks); 327 #endif 328 329 return (ks); 330 } 331 332 static struct ksem * 333 ksem_hold(struct ksem *ks) 334 { 335 336 refcount_acquire(&ks->ks_ref); 337 return (ks); 338 } 339 340 static void 341 ksem_drop(struct ksem *ks) 342 { 343 344 if (refcount_release(&ks->ks_ref)) { 345 #ifdef MAC 346 mac_posixsem_destroy(ks); 347 #endif 348 cv_destroy(&ks->ks_cv); 349 free(ks, M_KSEM); 350 mtx_lock(&ksem_count_lock); 351 nsems--; 352 mtx_unlock(&ksem_count_lock); 353 } 354 } 355 356 /* 357 * Determine if the credentials have sufficient permissions for read 358 * and write access. 359 */ 360 static int 361 ksem_access(struct ksem *ks, struct ucred *ucred) 362 { 363 int error; 364 365 error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid, 366 VREAD | VWRITE, ucred, NULL); 367 if (error) 368 error = priv_check_cred(ucred, PRIV_SEM_WRITE, 0); 369 return (error); 370 } 371 372 /* 373 * Dictionary management. We maintain an in-kernel dictionary to map 374 * paths to semaphore objects. We use the FNV hash on the path to 375 * store the mappings in a hash table. 376 */ 377 static struct ksem * 378 ksem_lookup(char *path, Fnv32_t fnv) 379 { 380 struct ksem_mapping *map; 381 382 LIST_FOREACH(map, KSEM_HASH(fnv), km_link) { 383 if (map->km_fnv != fnv) 384 continue; 385 if (strcmp(map->km_path, path) == 0) 386 return (map->km_ksem); 387 } 388 389 return (NULL); 390 } 391 392 static void 393 ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks) 394 { 395 struct ksem_mapping *map; 396 397 map = malloc(sizeof(struct ksem_mapping), M_KSEM, M_WAITOK); 398 map->km_path = path; 399 map->km_fnv = fnv; 400 map->km_ksem = ksem_hold(ks); 401 ks->ks_path = path; 402 LIST_INSERT_HEAD(KSEM_HASH(fnv), map, km_link); 403 } 404 405 static int 406 ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred) 407 { 408 struct ksem_mapping *map; 409 int error; 410 411 LIST_FOREACH(map, KSEM_HASH(fnv), km_link) { 412 if (map->km_fnv != fnv) 413 continue; 414 if (strcmp(map->km_path, path) == 0) { 415 #ifdef MAC 416 error = mac_posixsem_check_unlink(ucred, map->km_ksem); 417 if (error) 418 return (error); 419 #endif 420 error = ksem_access(map->km_ksem, ucred); 421 if (error) 422 return (error); 423 map->km_ksem->ks_path = NULL; 424 LIST_REMOVE(map, km_link); 425 ksem_drop(map->km_ksem); 426 free(map->km_path, M_KSEM); 427 free(map, M_KSEM); 428 return (0); 429 } 430 } 431 432 return (ENOENT); 433 } 434 435 static int 436 ksem_create_copyout_semid(struct thread *td, semid_t *semidp, int fd, 437 int compat32) 438 { 439 semid_t semid; 440 #ifdef COMPAT_FREEBSD32 441 int32_t semid32; 442 #endif 443 void *ptr; 444 size_t ptrs; 445 446 #ifdef COMPAT_FREEBSD32 447 if (compat32) { 448 semid32 = fd; 449 ptr = &semid32; 450 ptrs = sizeof(semid32); 451 } else { 452 #endif 453 semid = fd; 454 ptr = &semid; 455 ptrs = sizeof(semid); 456 compat32 = 0; /* silence gcc */ 457 #ifdef COMPAT_FREEBSD32 458 } 459 #endif 460 461 return (copyout(ptr, semidp, ptrs)); 462 } 463 464 /* Other helper routines. */ 465 static int 466 ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode, 467 unsigned int value, int flags, int compat32) 468 { 469 struct filedesc *fdp; 470 struct ksem *ks; 471 struct file *fp; 472 char *path; 473 const char *pr_path; 474 size_t pr_pathlen; 475 Fnv32_t fnv; 476 int error, fd; 477 478 AUDIT_ARG_FFLAGS(flags); 479 AUDIT_ARG_MODE(mode); 480 AUDIT_ARG_VALUE(value); 481 482 if (value > SEM_VALUE_MAX) 483 return (EINVAL); 484 485 fdp = td->td_proc->p_fd; 486 mode = (mode & ~fdp->fd_cmask) & ACCESSPERMS; 487 error = falloc(td, &fp, &fd, O_CLOEXEC); 488 if (error) { 489 if (name == NULL) 490 error = ENOSPC; 491 return (error); 492 } 493 494 /* 495 * Go ahead and copyout the file descriptor now. This is a bit 496 * premature, but it is a lot easier to handle errors as opposed 497 * to later when we've possibly created a new semaphore, etc. 498 */ 499 error = ksem_create_copyout_semid(td, semidp, fd, compat32); 500 if (error) { 501 fdclose(td, fp, fd); 502 fdrop(fp, td); 503 return (error); 504 } 505 506 if (name == NULL) { 507 /* Create an anonymous semaphore. */ 508 ks = ksem_alloc(td->td_ucred, mode, value); 509 if (ks == NULL) 510 error = ENOSPC; 511 else 512 ks->ks_flags |= KS_ANONYMOUS; 513 } else { 514 path = malloc(MAXPATHLEN, M_KSEM, M_WAITOK); 515 pr_path = td->td_ucred->cr_prison->pr_path; 516 517 /* Construct a full pathname for jailed callers. */ 518 pr_pathlen = strcmp(pr_path, "/") == 0 ? 0 519 : strlcpy(path, pr_path, MAXPATHLEN); 520 error = copyinstr(name, path + pr_pathlen, 521 MAXPATHLEN - pr_pathlen, NULL); 522 523 /* Require paths to start with a '/' character. */ 524 if (error == 0 && path[pr_pathlen] != '/') 525 error = EINVAL; 526 if (error) { 527 fdclose(td, fp, fd); 528 fdrop(fp, td); 529 free(path, M_KSEM); 530 return (error); 531 } 532 533 AUDIT_ARG_UPATH1_CANON(path); 534 fnv = fnv_32_str(path, FNV1_32_INIT); 535 sx_xlock(&ksem_dict_lock); 536 ks = ksem_lookup(path, fnv); 537 if (ks == NULL) { 538 /* Object does not exist, create it if requested. */ 539 if (flags & O_CREAT) { 540 ks = ksem_alloc(td->td_ucred, mode, value); 541 if (ks == NULL) 542 error = ENFILE; 543 else { 544 ksem_insert(path, fnv, ks); 545 path = NULL; 546 } 547 } else 548 error = ENOENT; 549 } else { 550 /* 551 * Object already exists, obtain a new 552 * reference if requested and permitted. 553 */ 554 if ((flags & (O_CREAT | O_EXCL)) == 555 (O_CREAT | O_EXCL)) 556 error = EEXIST; 557 else { 558 #ifdef MAC 559 error = mac_posixsem_check_open(td->td_ucred, 560 ks); 561 if (error == 0) 562 #endif 563 error = ksem_access(ks, td->td_ucred); 564 } 565 if (error == 0) 566 ksem_hold(ks); 567 #ifdef INVARIANTS 568 else 569 ks = NULL; 570 #endif 571 } 572 sx_xunlock(&ksem_dict_lock); 573 if (path) 574 free(path, M_KSEM); 575 } 576 577 if (error) { 578 KASSERT(ks == NULL, ("ksem_create error with a ksem")); 579 fdclose(td, fp, fd); 580 fdrop(fp, td); 581 return (error); 582 } 583 KASSERT(ks != NULL, ("ksem_create w/o a ksem")); 584 585 finit(fp, FREAD | FWRITE, DTYPE_SEM, ks, &ksem_ops); 586 587 fdrop(fp, td); 588 589 return (0); 590 } 591 592 static int 593 ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, 594 struct file **fpp) 595 { 596 struct ksem *ks; 597 struct file *fp; 598 int error; 599 600 error = fget(td, id, rightsp, &fp); 601 if (error) 602 return (EINVAL); 603 if (fp->f_type != DTYPE_SEM) { 604 fdrop(fp, td); 605 return (EINVAL); 606 } 607 ks = fp->f_data; 608 if (ks->ks_flags & KS_DEAD) { 609 fdrop(fp, td); 610 return (EINVAL); 611 } 612 *fpp = fp; 613 return (0); 614 } 615 616 /* System calls. */ 617 #ifndef _SYS_SYSPROTO_H_ 618 struct ksem_init_args { 619 unsigned int value; 620 semid_t *idp; 621 }; 622 #endif 623 int 624 sys_ksem_init(struct thread *td, struct ksem_init_args *uap) 625 { 626 627 return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value, 628 0, 0)); 629 } 630 631 #ifndef _SYS_SYSPROTO_H_ 632 struct ksem_open_args { 633 char *name; 634 int oflag; 635 mode_t mode; 636 unsigned int value; 637 semid_t *idp; 638 }; 639 #endif 640 int 641 sys_ksem_open(struct thread *td, struct ksem_open_args *uap) 642 { 643 644 DP((">>> ksem_open start, pid=%d\n", (int)td->td_proc->p_pid)); 645 646 if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0) 647 return (EINVAL); 648 return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value, 649 uap->oflag, 0)); 650 } 651 652 #ifndef _SYS_SYSPROTO_H_ 653 struct ksem_unlink_args { 654 char *name; 655 }; 656 #endif 657 int 658 sys_ksem_unlink(struct thread *td, struct ksem_unlink_args *uap) 659 { 660 char *path; 661 const char *pr_path; 662 size_t pr_pathlen; 663 Fnv32_t fnv; 664 int error; 665 666 path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 667 pr_path = td->td_ucred->cr_prison->pr_path; 668 pr_pathlen = strcmp(pr_path, "/") == 0 ? 0 669 : strlcpy(path, pr_path, MAXPATHLEN); 670 error = copyinstr(uap->name, path + pr_pathlen, MAXPATHLEN - pr_pathlen, 671 NULL); 672 if (error) { 673 free(path, M_TEMP); 674 return (error); 675 } 676 677 AUDIT_ARG_UPATH1_CANON(path); 678 fnv = fnv_32_str(path, FNV1_32_INIT); 679 sx_xlock(&ksem_dict_lock); 680 error = ksem_remove(path, fnv, td->td_ucred); 681 sx_xunlock(&ksem_dict_lock); 682 free(path, M_TEMP); 683 684 return (error); 685 } 686 687 #ifndef _SYS_SYSPROTO_H_ 688 struct ksem_close_args { 689 semid_t id; 690 }; 691 #endif 692 int 693 sys_ksem_close(struct thread *td, struct ksem_close_args *uap) 694 { 695 cap_rights_t rights; 696 struct ksem *ks; 697 struct file *fp; 698 int error; 699 700 /* No capability rights required to close a semaphore. */ 701 AUDIT_ARG_FD(uap->id); 702 error = ksem_get(td, uap->id, cap_rights_init(&rights), &fp); 703 if (error) 704 return (error); 705 ks = fp->f_data; 706 if (ks->ks_flags & KS_ANONYMOUS) { 707 fdrop(fp, td); 708 return (EINVAL); 709 } 710 error = kern_close(td, uap->id); 711 fdrop(fp, td); 712 return (error); 713 } 714 715 #ifndef _SYS_SYSPROTO_H_ 716 struct ksem_post_args { 717 semid_t id; 718 }; 719 #endif 720 int 721 sys_ksem_post(struct thread *td, struct ksem_post_args *uap) 722 { 723 cap_rights_t rights; 724 struct file *fp; 725 struct ksem *ks; 726 int error; 727 728 AUDIT_ARG_FD(uap->id); 729 error = ksem_get(td, uap->id, 730 cap_rights_init(&rights, CAP_SEM_POST), &fp); 731 if (error) 732 return (error); 733 ks = fp->f_data; 734 735 mtx_lock(&sem_lock); 736 #ifdef MAC 737 error = mac_posixsem_check_post(td->td_ucred, fp->f_cred, ks); 738 if (error) 739 goto err; 740 #endif 741 if (ks->ks_value == SEM_VALUE_MAX) { 742 error = EOVERFLOW; 743 goto err; 744 } 745 ++ks->ks_value; 746 if (ks->ks_waiters > 0) 747 cv_signal(&ks->ks_cv); 748 error = 0; 749 vfs_timestamp(&ks->ks_ctime); 750 err: 751 mtx_unlock(&sem_lock); 752 fdrop(fp, td); 753 return (error); 754 } 755 756 #ifndef _SYS_SYSPROTO_H_ 757 struct ksem_wait_args { 758 semid_t id; 759 }; 760 #endif 761 int 762 sys_ksem_wait(struct thread *td, struct ksem_wait_args *uap) 763 { 764 765 return (kern_sem_wait(td, uap->id, 0, NULL)); 766 } 767 768 #ifndef _SYS_SYSPROTO_H_ 769 struct ksem_timedwait_args { 770 semid_t id; 771 const struct timespec *abstime; 772 }; 773 #endif 774 int 775 sys_ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap) 776 { 777 struct timespec abstime; 778 struct timespec *ts; 779 int error; 780 781 /* 782 * We allow a null timespec (wait forever). 783 */ 784 if (uap->abstime == NULL) 785 ts = NULL; 786 else { 787 error = copyin(uap->abstime, &abstime, sizeof(abstime)); 788 if (error != 0) 789 return (error); 790 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0) 791 return (EINVAL); 792 ts = &abstime; 793 } 794 return (kern_sem_wait(td, uap->id, 0, ts)); 795 } 796 797 #ifndef _SYS_SYSPROTO_H_ 798 struct ksem_trywait_args { 799 semid_t id; 800 }; 801 #endif 802 int 803 sys_ksem_trywait(struct thread *td, struct ksem_trywait_args *uap) 804 { 805 806 return (kern_sem_wait(td, uap->id, 1, NULL)); 807 } 808 809 static int 810 kern_sem_wait(struct thread *td, semid_t id, int tryflag, 811 struct timespec *abstime) 812 { 813 struct timespec ts1, ts2; 814 struct timeval tv; 815 cap_rights_t rights; 816 struct file *fp; 817 struct ksem *ks; 818 int error; 819 820 DP((">>> kern_sem_wait entered! pid=%d\n", (int)td->td_proc->p_pid)); 821 AUDIT_ARG_FD(id); 822 error = ksem_get(td, id, cap_rights_init(&rights, CAP_SEM_WAIT), &fp); 823 if (error) 824 return (error); 825 ks = fp->f_data; 826 mtx_lock(&sem_lock); 827 DP((">>> kern_sem_wait critical section entered! pid=%d\n", 828 (int)td->td_proc->p_pid)); 829 #ifdef MAC 830 error = mac_posixsem_check_wait(td->td_ucred, fp->f_cred, ks); 831 if (error) { 832 DP(("kern_sem_wait mac failed\n")); 833 goto err; 834 } 835 #endif 836 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag)); 837 vfs_timestamp(&ks->ks_atime); 838 while (ks->ks_value == 0) { 839 ks->ks_waiters++; 840 if (tryflag != 0) 841 error = EAGAIN; 842 else if (abstime == NULL) 843 error = cv_wait_sig(&ks->ks_cv, &sem_lock); 844 else { 845 for (;;) { 846 ts1 = *abstime; 847 getnanotime(&ts2); 848 timespecsub(&ts1, &ts2); 849 TIMESPEC_TO_TIMEVAL(&tv, &ts1); 850 if (tv.tv_sec < 0) { 851 error = ETIMEDOUT; 852 break; 853 } 854 error = cv_timedwait_sig(&ks->ks_cv, 855 &sem_lock, tvtohz(&tv)); 856 if (error != EWOULDBLOCK) 857 break; 858 } 859 } 860 ks->ks_waiters--; 861 if (error) 862 goto err; 863 } 864 ks->ks_value--; 865 DP(("kern_sem_wait value post-decrement = %d\n", ks->ks_value)); 866 error = 0; 867 err: 868 mtx_unlock(&sem_lock); 869 fdrop(fp, td); 870 DP(("<<< kern_sem_wait leaving, pid=%d, error = %d\n", 871 (int)td->td_proc->p_pid, error)); 872 return (error); 873 } 874 875 #ifndef _SYS_SYSPROTO_H_ 876 struct ksem_getvalue_args { 877 semid_t id; 878 int *val; 879 }; 880 #endif 881 int 882 sys_ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap) 883 { 884 cap_rights_t rights; 885 struct file *fp; 886 struct ksem *ks; 887 int error, val; 888 889 AUDIT_ARG_FD(uap->id); 890 error = ksem_get(td, uap->id, 891 cap_rights_init(&rights, CAP_SEM_GETVALUE), &fp); 892 if (error) 893 return (error); 894 ks = fp->f_data; 895 896 mtx_lock(&sem_lock); 897 #ifdef MAC 898 error = mac_posixsem_check_getvalue(td->td_ucred, fp->f_cred, ks); 899 if (error) { 900 mtx_unlock(&sem_lock); 901 fdrop(fp, td); 902 return (error); 903 } 904 #endif 905 val = ks->ks_value; 906 vfs_timestamp(&ks->ks_atime); 907 mtx_unlock(&sem_lock); 908 fdrop(fp, td); 909 error = copyout(&val, uap->val, sizeof(val)); 910 return (error); 911 } 912 913 #ifndef _SYS_SYSPROTO_H_ 914 struct ksem_destroy_args { 915 semid_t id; 916 }; 917 #endif 918 int 919 sys_ksem_destroy(struct thread *td, struct ksem_destroy_args *uap) 920 { 921 cap_rights_t rights; 922 struct file *fp; 923 struct ksem *ks; 924 int error; 925 926 /* No capability rights required to close a semaphore. */ 927 AUDIT_ARG_FD(uap->id); 928 error = ksem_get(td, uap->id, cap_rights_init(&rights), &fp); 929 if (error) 930 return (error); 931 ks = fp->f_data; 932 if (!(ks->ks_flags & KS_ANONYMOUS)) { 933 fdrop(fp, td); 934 return (EINVAL); 935 } 936 mtx_lock(&sem_lock); 937 if (ks->ks_waiters != 0) { 938 mtx_unlock(&sem_lock); 939 error = EBUSY; 940 goto err; 941 } 942 ks->ks_flags |= KS_DEAD; 943 mtx_unlock(&sem_lock); 944 945 error = kern_close(td, uap->id); 946 err: 947 fdrop(fp, td); 948 return (error); 949 } 950 951 static struct syscall_helper_data ksem_syscalls[] = { 952 SYSCALL_INIT_HELPER(ksem_init), 953 SYSCALL_INIT_HELPER(ksem_open), 954 SYSCALL_INIT_HELPER(ksem_unlink), 955 SYSCALL_INIT_HELPER(ksem_close), 956 SYSCALL_INIT_HELPER(ksem_post), 957 SYSCALL_INIT_HELPER(ksem_wait), 958 SYSCALL_INIT_HELPER(ksem_timedwait), 959 SYSCALL_INIT_HELPER(ksem_trywait), 960 SYSCALL_INIT_HELPER(ksem_getvalue), 961 SYSCALL_INIT_HELPER(ksem_destroy), 962 SYSCALL_INIT_LAST 963 }; 964 965 #ifdef COMPAT_FREEBSD32 966 #include <compat/freebsd32/freebsd32.h> 967 #include <compat/freebsd32/freebsd32_proto.h> 968 #include <compat/freebsd32/freebsd32_signal.h> 969 #include <compat/freebsd32/freebsd32_syscall.h> 970 #include <compat/freebsd32/freebsd32_util.h> 971 972 int 973 freebsd32_ksem_init(struct thread *td, struct freebsd32_ksem_init_args *uap) 974 { 975 976 return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value, 977 0, 1)); 978 } 979 980 int 981 freebsd32_ksem_open(struct thread *td, struct freebsd32_ksem_open_args *uap) 982 { 983 984 if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0) 985 return (EINVAL); 986 return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value, 987 uap->oflag, 1)); 988 } 989 990 int 991 freebsd32_ksem_timedwait(struct thread *td, 992 struct freebsd32_ksem_timedwait_args *uap) 993 { 994 struct timespec32 abstime32; 995 struct timespec *ts, abstime; 996 int error; 997 998 /* 999 * We allow a null timespec (wait forever). 1000 */ 1001 if (uap->abstime == NULL) 1002 ts = NULL; 1003 else { 1004 error = copyin(uap->abstime, &abstime32, sizeof(abstime32)); 1005 if (error != 0) 1006 return (error); 1007 CP(abstime32, abstime, tv_sec); 1008 CP(abstime32, abstime, tv_nsec); 1009 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0) 1010 return (EINVAL); 1011 ts = &abstime; 1012 } 1013 return (kern_sem_wait(td, uap->id, 0, ts)); 1014 } 1015 1016 static struct syscall_helper_data ksem32_syscalls[] = { 1017 SYSCALL32_INIT_HELPER(freebsd32_ksem_init), 1018 SYSCALL32_INIT_HELPER(freebsd32_ksem_open), 1019 SYSCALL32_INIT_HELPER_COMPAT(ksem_unlink), 1020 SYSCALL32_INIT_HELPER_COMPAT(ksem_close), 1021 SYSCALL32_INIT_HELPER_COMPAT(ksem_post), 1022 SYSCALL32_INIT_HELPER_COMPAT(ksem_wait), 1023 SYSCALL32_INIT_HELPER(freebsd32_ksem_timedwait), 1024 SYSCALL32_INIT_HELPER_COMPAT(ksem_trywait), 1025 SYSCALL32_INIT_HELPER_COMPAT(ksem_getvalue), 1026 SYSCALL32_INIT_HELPER_COMPAT(ksem_destroy), 1027 SYSCALL_INIT_LAST 1028 }; 1029 #endif 1030 1031 static int 1032 ksem_module_init(void) 1033 { 1034 int error; 1035 1036 mtx_init(&sem_lock, "sem", NULL, MTX_DEF); 1037 mtx_init(&ksem_count_lock, "ksem count", NULL, MTX_DEF); 1038 sx_init(&ksem_dict_lock, "ksem dictionary"); 1039 ksem_dictionary = hashinit(1024, M_KSEM, &ksem_hash); 1040 p31b_setcfg(CTL_P1003_1B_SEMAPHORES, 200112L); 1041 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX); 1042 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX); 1043 1044 error = syscall_helper_register(ksem_syscalls, SY_THR_STATIC_KLD); 1045 if (error) 1046 return (error); 1047 #ifdef COMPAT_FREEBSD32 1048 error = syscall32_helper_register(ksem32_syscalls, SY_THR_STATIC_KLD); 1049 if (error) 1050 return (error); 1051 #endif 1052 return (0); 1053 } 1054 1055 static void 1056 ksem_module_destroy(void) 1057 { 1058 1059 #ifdef COMPAT_FREEBSD32 1060 syscall32_helper_unregister(ksem32_syscalls); 1061 #endif 1062 syscall_helper_unregister(ksem_syscalls); 1063 1064 p31b_setcfg(CTL_P1003_1B_SEMAPHORES, 0); 1065 hashdestroy(ksem_dictionary, M_KSEM, ksem_hash); 1066 sx_destroy(&ksem_dict_lock); 1067 mtx_destroy(&ksem_count_lock); 1068 mtx_destroy(&sem_lock); 1069 p31b_unsetcfg(CTL_P1003_1B_SEM_VALUE_MAX); 1070 p31b_unsetcfg(CTL_P1003_1B_SEM_NSEMS_MAX); 1071 } 1072 1073 static int 1074 sem_modload(struct module *module, int cmd, void *arg) 1075 { 1076 int error = 0; 1077 1078 switch (cmd) { 1079 case MOD_LOAD: 1080 error = ksem_module_init(); 1081 if (error) 1082 ksem_module_destroy(); 1083 break; 1084 1085 case MOD_UNLOAD: 1086 mtx_lock(&ksem_count_lock); 1087 if (nsems != 0) { 1088 error = EOPNOTSUPP; 1089 mtx_unlock(&ksem_count_lock); 1090 break; 1091 } 1092 ksem_dead = 1; 1093 mtx_unlock(&ksem_count_lock); 1094 ksem_module_destroy(); 1095 break; 1096 1097 case MOD_SHUTDOWN: 1098 break; 1099 default: 1100 error = EINVAL; 1101 break; 1102 } 1103 return (error); 1104 } 1105 1106 static moduledata_t sem_mod = { 1107 "sem", 1108 &sem_modload, 1109 NULL 1110 }; 1111 1112 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 1113 MODULE_VERSION(sem, 1); 1114