1 /* $NetBSD: uipc_sem.c,v 1.33 2011/04/15 00:01:48 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 /* 59 * Implementation of POSIX semaphore. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.33 2011/04/15 00:01:48 rmind Exp $"); 64 65 #include <sys/param.h> 66 #include <sys/systm.h> 67 #include <sys/kernel.h> 68 #include <sys/proc.h> 69 #include <sys/ksem.h> 70 #include <sys/syscall.h> 71 #include <sys/stat.h> 72 #include <sys/kmem.h> 73 #include <sys/fcntl.h> 74 #include <sys/file.h> 75 #include <sys/filedesc.h> 76 #include <sys/kauth.h> 77 #include <sys/module.h> 78 #include <sys/mount.h> 79 #include <sys/syscall.h> 80 #include <sys/syscallargs.h> 81 #include <sys/syscallvar.h> 82 83 MODULE(MODULE_CLASS_MISC, ksem, NULL); 84 85 #define SEM_MAX_NAMELEN 14 86 #define SEM_VALUE_MAX (~0U) 87 88 #define KS_UNLINKED 0x01 89 90 typedef struct ksem { 91 LIST_ENTRY(ksem) ks_entry; /* global list entry */ 92 kmutex_t ks_lock; /* lock on this ksem */ 93 kcondvar_t ks_cv; /* condition variable */ 94 u_int ks_ref; /* number of references */ 95 u_int ks_value; /* current value */ 96 u_int ks_waiters; /* number of waiters */ 97 char * ks_name; /* name, if named */ 98 size_t ks_namelen; /* length of name */ 99 int ks_flags; /* for KS_UNLINKED */ 100 mode_t ks_mode; /* protection bits */ 101 uid_t ks_uid; /* creator uid */ 102 gid_t ks_gid; /* creator gid */ 103 } ksem_t; 104 105 static kmutex_t ksem_lock __cacheline_aligned; 106 static LIST_HEAD(,ksem) ksem_head __cacheline_aligned; 107 static u_int nsems __cacheline_aligned; 108 109 static int ksem_sysinit(void); 110 static int ksem_sysfini(bool); 111 static int ksem_modcmd(modcmd_t, void *); 112 static int ksem_close_fop(file_t *); 113 114 static const struct fileops semops = { 115 .fo_read = fbadop_read, 116 .fo_write = fbadop_write, 117 .fo_ioctl = fbadop_ioctl, 118 .fo_fcntl = fnullop_fcntl, 119 .fo_poll = fnullop_poll, 120 .fo_stat = fbadop_stat, 121 .fo_close = ksem_close_fop, 122 .fo_kqfilter = fnullop_kqfilter, 123 .fo_restart = fnullop_restart, 124 }; 125 126 static const struct syscall_package ksem_syscalls[] = { 127 { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init }, 128 { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open }, 129 { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink }, 130 { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close }, 131 { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post }, 132 { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait }, 133 { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait }, 134 { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue }, 135 { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy }, 136 { 0, 0, NULL }, 137 }; 138 139 static int 140 ksem_sysinit(void) 141 { 142 int error; 143 144 nsems = 0; 145 mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE); 146 LIST_INIT(&ksem_head); 147 148 error = syscall_establish(NULL, ksem_syscalls); 149 if (error) { 150 (void)ksem_sysfini(false); 151 } 152 return error; 153 } 154 155 static int 156 ksem_sysfini(bool interface) 157 { 158 int error; 159 160 if (interface) { 161 error = syscall_disestablish(NULL, ksem_syscalls); 162 if (error != 0) { 163 return error; 164 } 165 if (nsems != 0) { 166 error = syscall_establish(NULL, ksem_syscalls); 167 KASSERT(error == 0); 168 return EBUSY; 169 } 170 } 171 mutex_destroy(&ksem_lock); 172 return 0; 173 } 174 175 static int 176 ksem_modcmd(modcmd_t cmd, void *arg) 177 { 178 179 switch (cmd) { 180 case MODULE_CMD_INIT: 181 return ksem_sysinit(); 182 183 case MODULE_CMD_FINI: 184 return ksem_sysfini(true); 185 186 default: 187 return ENOTTY; 188 } 189 } 190 191 static ksem_t * 192 ksem_lookup(const char *name) 193 { 194 ksem_t *ks; 195 196 KASSERT(mutex_owned(&ksem_lock)); 197 198 LIST_FOREACH(ks, &ksem_head, ks_entry) { 199 if (strcmp(ks->ks_name, name) == 0) { 200 mutex_enter(&ks->ks_lock); 201 return ks; 202 } 203 } 204 return NULL; 205 } 206 207 static int 208 ksem_perm(lwp_t *l, ksem_t *ks) 209 { 210 kauth_cred_t uc = l->l_cred; 211 mode_t mode = ks->ks_mode; 212 213 KASSERT(mutex_owned(&ks->ks_lock)); 214 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (mode & S_IWUSR) != 0) || 215 (kauth_cred_getegid(uc) == ks->ks_gid && (mode & S_IWGRP) != 0) || 216 (mode & S_IWOTH) != 0 || 217 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, NULL) == 0) 218 return 0; 219 220 return EACCES; 221 } 222 223 /* 224 * ksem_get: get the semaphore from the descriptor. 225 * 226 * => locks the semaphore, if found. 227 * => holds a reference on the file descriptor. 228 */ 229 static int 230 ksem_get(int fd, ksem_t **ksret) 231 { 232 ksem_t *ks; 233 file_t *fp; 234 235 fp = fd_getfile(fd); 236 if (__predict_false(fp == NULL)) { 237 return EBADF; 238 } 239 if (__predict_false(fp->f_type != DTYPE_SEM)) { 240 fd_putfile(fd); 241 return EBADF; 242 } 243 ks = fp->f_data; 244 mutex_enter(&ks->ks_lock); 245 246 *ksret = ks; 247 return 0; 248 } 249 250 /* 251 * ksem_create: allocate and setup a new semaphore structure. 252 */ 253 static int 254 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val) 255 { 256 ksem_t *ks; 257 kauth_cred_t uc; 258 char *kname; 259 size_t len; 260 261 /* Pre-check for the limit. */ 262 if (nsems >= ksem_max) { 263 return ENFILE; 264 } 265 266 if (val > SEM_VALUE_MAX) { 267 return EINVAL; 268 } 269 270 if (name != NULL) { 271 len = strlen(name); 272 if (len > SEM_MAX_NAMELEN) { 273 return ENAMETOOLONG; 274 } 275 /* Name must start with a '/' but not contain one. */ 276 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 277 return EINVAL; 278 } 279 kname = kmem_alloc(++len, KM_SLEEP); 280 strlcpy(kname, name, len); 281 } else { 282 kname = NULL; 283 len = 0; 284 } 285 286 ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP); 287 mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE); 288 cv_init(&ks->ks_cv, "psem"); 289 ks->ks_name = kname; 290 ks->ks_namelen = len; 291 ks->ks_mode = mode; 292 ks->ks_value = val; 293 ks->ks_ref = 1; 294 295 uc = l->l_cred; 296 ks->ks_uid = kauth_cred_geteuid(uc); 297 ks->ks_gid = kauth_cred_getegid(uc); 298 299 *ksret = ks; 300 return 0; 301 } 302 303 static void 304 ksem_free(ksem_t *ks) 305 { 306 307 if (ks->ks_name) { 308 KASSERT(ks->ks_namelen > 0); 309 kmem_free(ks->ks_name, ks->ks_namelen); 310 } 311 mutex_destroy(&ks->ks_lock); 312 cv_destroy(&ks->ks_cv); 313 kmem_free(ks, sizeof(ksem_t)); 314 } 315 316 int 317 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, 318 register_t *retval) 319 { 320 /* { 321 unsigned int value; 322 intptr_t *idp; 323 } */ 324 325 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); 326 } 327 328 int 329 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout) 330 { 331 proc_t *p = l->l_proc; 332 ksem_t *ks; 333 file_t *fp; 334 intptr_t id; 335 int fd, error; 336 337 error = fd_allocfile(&fp, &fd); 338 if (error) { 339 return error; 340 } 341 fp->f_type = DTYPE_SEM; 342 fp->f_flag = FREAD | FWRITE; 343 fp->f_ops = &semops; 344 345 id = (intptr_t)fd; 346 error = (*docopyout)(&id, idp, sizeof(*idp)); 347 if (error) { 348 fd_abort(p, fp, fd); 349 return error; 350 } 351 352 /* Note the mode does not matter for anonymous semaphores. */ 353 error = ksem_create(l, NULL, &ks, 0, val); 354 if (error) { 355 fd_abort(p, fp, fd); 356 return error; 357 } 358 fp->f_data = ks; 359 fd_affix(p, fp, fd); 360 return error; 361 } 362 363 int 364 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, 365 register_t *retval) 366 { 367 /* { 368 const char *name; 369 int oflag; 370 mode_t mode; 371 unsigned int value; 372 intptr_t *idp; 373 } */ 374 375 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 376 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 377 } 378 379 int 380 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 381 unsigned int value, intptr_t *idp, copyout_t docopyout) 382 { 383 char name[SEM_MAX_NAMELEN + 1]; 384 proc_t *p = l->l_proc; 385 ksem_t *ksnew = NULL, *ks; 386 file_t *fp; 387 intptr_t id; 388 int fd, error; 389 390 error = copyinstr(semname, name, sizeof(name), NULL); 391 if (error) { 392 return error; 393 } 394 error = fd_allocfile(&fp, &fd); 395 if (error) { 396 return error; 397 } 398 fp->f_type = DTYPE_SEM; 399 fp->f_flag = FREAD | FWRITE; 400 fp->f_ops = &semops; 401 402 /* 403 * The ID (file descriptor number) can be stored early. 404 * Note that zero is a special value for libpthread. 405 */ 406 id = (intptr_t)fd; 407 error = (*docopyout)(&id, idp, sizeof(*idp)); 408 if (error) { 409 goto err; 410 } 411 412 if (oflag & O_CREAT) { 413 /* Create a new semaphore. */ 414 error = ksem_create(l, name, &ksnew, mode, value); 415 if (error) { 416 goto err; 417 } 418 KASSERT(ksnew != NULL); 419 } 420 421 /* Lookup for a semaphore with such name. */ 422 mutex_enter(&ksem_lock); 423 ks = ksem_lookup(name); 424 if (ks) { 425 KASSERT(mutex_owned(&ks->ks_lock)); 426 mutex_exit(&ksem_lock); 427 428 /* Check for exclusive create. */ 429 if (oflag & O_EXCL) { 430 mutex_exit(&ks->ks_lock); 431 error = EEXIST; 432 goto err; 433 } 434 /* 435 * Verify permissions. If we can access it, 436 * add the reference of this thread. 437 */ 438 error = ksem_perm(l, ks); 439 if (error == 0) { 440 ks->ks_ref++; 441 } 442 mutex_exit(&ks->ks_lock); 443 if (error) { 444 goto err; 445 } 446 } else { 447 /* Fail if not found and not creating. */ 448 if ((oflag & O_CREAT) == 0) { 449 mutex_exit(&ksem_lock); 450 KASSERT(ksnew == NULL); 451 error = ENOENT; 452 goto err; 453 } 454 455 /* Check for the limit locked. */ 456 if (nsems >= ksem_max) { 457 mutex_exit(&ksem_lock); 458 error = ENFILE; 459 goto err; 460 } 461 462 /* 463 * Finally, insert semaphore into the list. 464 * Note: it already has the initial reference. 465 */ 466 ks = ksnew; 467 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); 468 nsems++; 469 mutex_exit(&ksem_lock); 470 471 ksnew = NULL; 472 } 473 KASSERT(ks != NULL); 474 fp->f_data = ks; 475 fd_affix(p, fp, fd); 476 err: 477 if (error) { 478 fd_abort(p, fp, fd); 479 } 480 if (ksnew) { 481 ksem_free(ksnew); 482 } 483 return error; 484 } 485 486 int 487 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, 488 register_t *retval) 489 { 490 /* { 491 intptr_t id; 492 } */ 493 int fd = (int)SCARG(uap, id); 494 495 if (fd_getfile(fd) == NULL) { 496 return EBADF; 497 } 498 return fd_close(fd); 499 } 500 501 static int 502 ksem_close_fop(file_t *fp) 503 { 504 ksem_t *ks = fp->f_data; 505 bool destroy = false; 506 507 mutex_enter(&ks->ks_lock); 508 KASSERT(ks->ks_ref > 0); 509 if (--ks->ks_ref == 0) { 510 /* 511 * Destroy if the last reference and semaphore is unnamed, 512 * or unlinked (for named semaphore). 513 */ 514 destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL); 515 } 516 mutex_exit(&ks->ks_lock); 517 518 if (destroy) { 519 ksem_free(ks); 520 } 521 return 0; 522 } 523 524 int 525 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, 526 register_t *retval) 527 { 528 /* { 529 const char *name; 530 } */ 531 char name[SEM_MAX_NAMELEN + 1]; 532 ksem_t *ks; 533 u_int refcnt; 534 int error; 535 536 error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL); 537 if (error) 538 return error; 539 540 mutex_enter(&ksem_lock); 541 ks = ksem_lookup(name); 542 if (ks == NULL) { 543 mutex_exit(&ksem_lock); 544 return ENOENT; 545 } 546 KASSERT(mutex_owned(&ks->ks_lock)); 547 548 /* Verify permissions. */ 549 error = ksem_perm(l, ks); 550 if (error) { 551 mutex_exit(&ks->ks_lock); 552 mutex_exit(&ksem_lock); 553 return error; 554 } 555 556 /* Remove from the global list. */ 557 LIST_REMOVE(ks, ks_entry); 558 nsems--; 559 mutex_exit(&ksem_lock); 560 561 refcnt = ks->ks_ref; 562 if (refcnt) { 563 /* Mark as unlinked, if there are references. */ 564 ks->ks_flags |= KS_UNLINKED; 565 } 566 mutex_exit(&ks->ks_lock); 567 568 if (refcnt == 0) { 569 ksem_free(ks); 570 } 571 return 0; 572 } 573 574 int 575 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, 576 register_t *retval) 577 { 578 /* { 579 intptr_t id; 580 } */ 581 int fd = (int)SCARG(uap, id), error; 582 ksem_t *ks; 583 584 error = ksem_get(fd, &ks); 585 if (error) { 586 return error; 587 } 588 KASSERT(mutex_owned(&ks->ks_lock)); 589 if (ks->ks_value == SEM_VALUE_MAX) { 590 error = EOVERFLOW; 591 goto out; 592 } 593 ks->ks_value++; 594 if (ks->ks_waiters) { 595 cv_broadcast(&ks->ks_cv); 596 } 597 out: 598 mutex_exit(&ks->ks_lock); 599 fd_putfile(fd); 600 return error; 601 } 602 603 static int 604 ksem_wait(lwp_t *l, intptr_t id, bool try) 605 { 606 int fd = (int)id, error; 607 ksem_t *ks; 608 609 error = ksem_get(fd, &ks); 610 if (error) { 611 return error; 612 } 613 KASSERT(mutex_owned(&ks->ks_lock)); 614 while (ks->ks_value == 0) { 615 ks->ks_waiters++; 616 error = try ? EAGAIN : cv_wait_sig(&ks->ks_cv, &ks->ks_lock); 617 ks->ks_waiters--; 618 if (error) 619 goto out; 620 } 621 ks->ks_value--; 622 out: 623 mutex_exit(&ks->ks_lock); 624 fd_putfile(fd); 625 return error; 626 } 627 628 int 629 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, 630 register_t *retval) 631 { 632 /* { 633 intptr_t id; 634 } */ 635 636 return ksem_wait(l, SCARG(uap, id), false); 637 } 638 639 int 640 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, 641 register_t *retval) 642 { 643 /* { 644 intptr_t id; 645 } */ 646 647 return ksem_wait(l, SCARG(uap, id), true); 648 } 649 650 int 651 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, 652 register_t *retval) 653 { 654 /* { 655 intptr_t id; 656 unsigned int *value; 657 } */ 658 int fd = (int)SCARG(uap, id), error; 659 ksem_t *ks; 660 unsigned int val; 661 662 error = ksem_get(fd, &ks); 663 if (error) { 664 return error; 665 } 666 KASSERT(mutex_owned(&ks->ks_lock)); 667 val = ks->ks_value; 668 mutex_exit(&ks->ks_lock); 669 fd_putfile(fd); 670 671 return copyout(&val, SCARG(uap, value), sizeof(val)); 672 } 673 674 int 675 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, 676 register_t *retval) 677 { 678 /* { 679 intptr_t id; 680 } */ 681 int fd = (int)SCARG(uap, id), error; 682 ksem_t *ks; 683 684 error = ksem_get(fd, &ks); 685 if (error) { 686 return error; 687 } 688 KASSERT(mutex_owned(&ks->ks_lock)); 689 690 /* Operation is only for unnamed semaphores. */ 691 if (ks->ks_name != NULL) { 692 error = EINVAL; 693 goto out; 694 } 695 /* Cannot destroy if there are waiters. */ 696 if (ks->ks_waiters) { 697 error = EBUSY; 698 goto out; 699 } 700 out: 701 mutex_exit(&ks->ks_lock); 702 if (error) { 703 fd_putfile(fd); 704 return error; 705 } 706 return fd_close(fd); 707 } 708