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