1/* 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * Copyright (c) 1990, 1996-1998 Apple Computer, Inc. 30 * All Rights Reserved. 31 */ 32/* 33 * posix_sem.c : Support for POSIX semaphore APIs 34 * 35 * File: posix_sem.c 36 * Author: Ananthakrishna Ramesh 37 * 38 * HISTORY 39 * 2-Sep-1999 A.Ramesh 40 * Created for MacOSX 41 * 42 */ 43/* 44 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 45 * support for mandatory and extensible security protections. This notice 46 * is included in support of clause 2.2 (b) of the Apple Public License, 47 * Version 2.0. 48 */ 49 50#include <sys/cdefs.h> 51#include <sys/param.h> 52#include <sys/systm.h> 53#include <sys/kernel.h> 54#include <sys/file_internal.h> 55#include <sys/filedesc.h> 56#include <sys/stat.h> 57#include <sys/proc_internal.h> 58#include <sys/kauth.h> 59#include <sys/mount.h> 60#include <sys/namei.h> 61#include <sys/vnode.h> 62#include <sys/ioctl.h> 63#include <sys/tty.h> 64#include <sys/malloc.h> 65#include <sys/semaphore.h> 66#include <sys/sysproto.h> 67#include <sys/proc_info.h> 68 69#if CONFIG_MACF 70#include <sys/vnode_internal.h> 71#include <security/mac_framework.h> 72#endif 73 74#include <security/audit/audit.h> 75 76#include <mach/mach_types.h> 77#include <mach/vm_prot.h> 78#include <mach/semaphore.h> 79#include <mach/sync_policy.h> 80#include <mach/task.h> 81#include <kern/kern_types.h> 82#include <kern/task.h> 83#include <kern/clock.h> 84#include <mach/kern_return.h> 85 86 87#define f_flag f_fglob->fg_flag 88#define f_type f_fglob->fg_ops->fo_type 89#define f_msgcount f_fglob->fg_msgcount 90#define f_cred f_fglob->fg_cred 91#define f_ops f_fglob->fg_ops 92#define f_offset f_fglob->fg_offset 93#define f_data f_fglob->fg_data 94#define PSEMNAMLEN 31 /* maximum name segment length we bother with */ 95 96struct pseminfo { 97 unsigned int psem_flags; 98 unsigned int psem_usecount; 99 mode_t psem_mode; 100 uid_t psem_uid; 101 gid_t psem_gid; 102 char psem_name[PSEMNAMLEN + 1]; /* segment name */ 103 semaphore_t psem_semobject; 104 struct label * psem_label; 105 pid_t psem_creator_pid; 106 uint64_t psem_creator_uniqueid; 107}; 108#define PSEMINFO_NULL (struct pseminfo *)0 109 110#define PSEM_NONE 1 111#define PSEM_DEFINED 2 112#define PSEM_ALLOCATED 4 113#define PSEM_MAPPED 8 114#define PSEM_INUSE 0x10 115#define PSEM_REMOVED 0x20 116#define PSEM_INCREATE 0x40 117#define PSEM_INDELETE 0x80 118 119struct psemcache { 120 LIST_ENTRY(psemcache) psem_hash; /* hash chain */ 121 struct pseminfo *pseminfo; /* vnode the name refers to */ 122 int psem_nlen; /* length of name */ 123 char psem_name[PSEMNAMLEN + 1]; /* segment name */ 124}; 125#define PSEMCACHE_NULL (struct psemcache *)0 126 127struct psemstats { 128 long goodhits; /* hits that we can really use */ 129 long neghits; /* negative hits that we can use */ 130 long badhits; /* hits we must drop */ 131 long falsehits; /* hits with id mismatch */ 132 long miss; /* misses */ 133 long longnames; /* long names that ignore cache */ 134}; 135 136struct psemname { 137 char *psem_nameptr; /* pointer to looked up name */ 138 long psem_namelen; /* length of looked up component */ 139 u_int32_t psem_hash; /* hash value of looked up name */ 140}; 141 142struct psemnode { 143 struct pseminfo *pinfo; 144#if DIAGNOSTIC 145 unsigned int readcnt; 146 unsigned int writecnt; 147#endif 148}; 149#define PSEMNODE_NULL (struct psemnode *)0 150 151 152#define PSEMHASH(pnp) \ 153 (&psemhashtbl[(pnp)->psem_hash & psemhash]) 154LIST_HEAD(psemhashhead, psemcache) *psemhashtbl; /* Hash Table */ 155u_long psemhash; /* size of hash table - 1 */ 156long psemnument; /* number of cache entries allocated */ 157long posix_sem_max = 10000; /* tunable for max POSIX semaphores */ 158 /* 10000 limits to ~1M of memory */ 159SYSCTL_NODE(_kern, KERN_POSIX, posix, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Posix"); 160SYSCTL_NODE(_kern_posix, OID_AUTO, sem, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Semaphores"); 161SYSCTL_LONG (_kern_posix_sem, OID_AUTO, max, CTLFLAG_RW | CTLFLAG_LOCKED, &posix_sem_max, "max"); 162 163struct psemstats psemstats; /* cache effectiveness statistics */ 164 165static int psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred); 166static int psem_cache_search(struct pseminfo **, 167 struct psemname *, struct psemcache **); 168static int psem_delete(struct pseminfo * pinfo); 169 170static int psem_read (struct fileproc *fp, struct uio *uio, 171 int flags, vfs_context_t ctx); 172static int psem_write (struct fileproc *fp, struct uio *uio, 173 int flags, vfs_context_t ctx); 174static int psem_ioctl (struct fileproc *fp, u_long com, 175 caddr_t data, vfs_context_t ctx); 176static int psem_select (struct fileproc *fp, int which, void *wql, vfs_context_t ctx); 177static int psem_closefile (struct fileglob *fp, vfs_context_t ctx); 178 179static int psem_kqfilter (struct fileproc *fp, struct knote *kn, vfs_context_t ctx); 180 181static const struct fileops psemops = { 182 DTYPE_PSXSEM, 183 psem_read, 184 psem_write, 185 psem_ioctl, 186 psem_select, 187 psem_closefile, 188 psem_kqfilter, 189 NULL 190}; 191 192static lck_grp_t *psx_sem_subsys_lck_grp; 193static lck_grp_attr_t *psx_sem_subsys_lck_grp_attr; 194static lck_attr_t *psx_sem_subsys_lck_attr; 195static lck_mtx_t psx_sem_subsys_mutex; 196 197#define PSEM_SUBSYS_LOCK() lck_mtx_lock(& psx_sem_subsys_mutex) 198#define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_sem_subsys_mutex) 199 200 201static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp); 202/* Initialize the mutex governing access to the posix sem subsystem */ 203__private_extern__ void 204psem_lock_init( void ) 205{ 206 207 psx_sem_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); 208 209 psx_sem_subsys_lck_grp = lck_grp_alloc_init("posix shared memory", psx_sem_subsys_lck_grp_attr); 210 211 psx_sem_subsys_lck_attr = lck_attr_alloc_init(); 212 lck_mtx_init(& psx_sem_subsys_mutex, psx_sem_subsys_lck_grp, psx_sem_subsys_lck_attr); 213} 214 215/* 216 * Lookup an entry in the cache 217 * 218 * 219 * status of -1 is returned if matches 220 * If the lookup determines that the name does not exist 221 * (negative cacheing), a status of ENOENT is returned. If the lookup 222 * fails, a status of zero is returned. 223 */ 224 225static int 226psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, 227 struct psemcache **pcache) 228{ 229 struct psemcache *pcp, *nnp; 230 struct psemhashhead *pcpp; 231 232 if (pnp->psem_namelen > PSEMNAMLEN) { 233 psemstats.longnames++; 234 return (0); 235 } 236 237 pcpp = PSEMHASH(pnp); 238 for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) { 239 nnp = pcp->psem_hash.le_next; 240 if (pcp->psem_nlen == pnp->psem_namelen && 241 !bcmp(pcp->psem_name, pnp->psem_nameptr, (u_int)pcp-> psem_nlen)) 242 break; 243 } 244 245 if (pcp == 0) { 246 psemstats.miss++; 247 return (0); 248 } 249 250 /* We found a "positive" match, return the vnode */ 251 if (pcp->pseminfo) { 252 psemstats.goodhits++; 253 /* TOUCH(ncp); */ 254 *psemp = pcp->pseminfo; 255 *pcache = pcp; 256 return (-1); 257 } 258 259 /* 260 * We found a "negative" match, ENOENT notifies client of this match. 261 * The nc_vpid field records whether this is a whiteout. 262 */ 263 psemstats.neghits++; 264 return (ENOENT); 265} 266 267/* 268 * Add an entry to the cache. 269 */ 270static int 271psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp) 272{ 273 struct psemhashhead *pcpp; 274 struct pseminfo *dpinfo; 275 struct psemcache *dpcp; 276 277#if DIAGNOSTIC 278 if (pnp->psem_namelen > PSEMNAMLEN) 279 panic("cache_enter: name too long"); 280#endif 281 282 283 /* if the entry has already been added by some one else return */ 284 if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) { 285 return(EEXIST); 286 } 287 if (psemnument >= posix_sem_max) 288 return(ENOSPC); 289 psemnument++; 290 /* 291 * Fill in cache info, if vp is NULL this is a "negative" cache entry. 292 * For negative entries, we have to record whether it is a whiteout. 293 * the whiteout flag is stored in the nc_vpid field which is 294 * otherwise unused. 295 */ 296 pcp->pseminfo = psemp; 297 pcp->psem_nlen = pnp->psem_namelen; 298 bcopy(pnp->psem_nameptr, pcp->psem_name, (unsigned)pcp->psem_nlen); 299 pcpp = PSEMHASH(pnp); 300#if DIAGNOSTIC 301 { 302 struct psemcache *p; 303 304 for (p = pcpp->lh_first; p != 0; p = p->psem_hash.le_next) 305 if (p == pcp) 306 panic("psem:cache_enter duplicate"); 307 } 308#endif 309 LIST_INSERT_HEAD(pcpp, pcp, psem_hash); 310 return(0); 311} 312 313/* 314 * Name cache initialization, from vfs_init() when we are booting 315 */ 316void 317psem_cache_init(void) 318{ 319 psemhashtbl = hashinit(posix_sem_max / 2, M_SHM, &psemhash); 320} 321 322static void 323psem_cache_delete(struct psemcache *pcp) 324{ 325#if DIAGNOSTIC 326 if (pcp->psem_hash.le_prev == 0) 327 panic("psem namecache purge le_prev"); 328 if (pcp->psem_hash.le_next == pcp) 329 panic("namecache purge le_next"); 330#endif /* DIAGNOSTIC */ 331 LIST_REMOVE(pcp, psem_hash); 332 pcp->psem_hash.le_prev = NULL; 333 psemnument--; 334} 335 336#if NOT_USED 337/* 338 * Invalidate a all entries to particular vnode. 339 * 340 * We actually just increment the v_id, that will do it. The entries will 341 * be purged by lookup as they get found. If the v_id wraps around, we 342 * need to ditch the entire cache, to avoid confusion. No valid vnode will 343 * ever have (v_id == 0). 344 */ 345static void 346psem_cache_purge(void) 347{ 348 struct psemcache *pcp; 349 struct psemhashhead *pcpp; 350 351 for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) { 352 while ( (pcp = pcpp->lh_first) ) 353 psem_cache_delete(pcp); 354 } 355} 356#endif /* NOT_USED */ 357 358int 359sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval) 360{ 361 size_t i; 362 int indx, error; 363 struct psemname nd; 364 struct pseminfo *pinfo; 365 struct fileproc *fp = NULL; 366 char *pnbuf = NULL; 367 struct pseminfo *new_pinfo = PSEMINFO_NULL; 368 struct psemnode *new_pnode = PSEMNODE_NULL; 369 struct psemcache *pcache = PSEMCACHE_NULL; 370 char * nameptr; 371 char * cp; 372 size_t pathlen, plen; 373 int fmode ; 374 int cmode = uap->mode; 375 int value = uap->value; 376 int incache = 0; 377 struct psemcache *pcp = PSEMCACHE_NULL; 378 kern_return_t kret = KERN_INVALID_ADDRESS; /* default fail */ 379 380 AUDIT_ARG(fflags, uap->oflag); 381 AUDIT_ARG(mode, uap->mode); 382 AUDIT_ARG(value32, uap->value); 383 384 pinfo = PSEMINFO_NULL; 385 386 /* 387 * Preallocate everything we might need up front to avoid taking 388 * and dropping the lock, opening us up to race conditions. 389 */ 390 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 391 if (pnbuf == NULL) { 392 error = ENOSPC; 393 goto bad; 394 } 395 396 pathlen = MAXPATHLEN; 397 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); 398 if (error) { 399 goto bad; 400 } 401 AUDIT_ARG(text, pnbuf); 402 if ( (pathlen > PSEMNAMLEN) ) { 403 error = ENAMETOOLONG; 404 goto bad; 405 } 406 407#ifdef PSXSEM_NAME_RESTRICT 408 nameptr = pnbuf; 409 if (*nameptr == '/') { 410 while (*(nameptr++) == '/') { 411 plen--; 412 error = EINVAL; 413 goto bad; 414 } 415 } else { 416 error = EINVAL; 417 goto bad; 418 } 419#endif /* PSXSEM_NAME_RESTRICT */ 420 421 plen = pathlen; 422 nameptr = pnbuf; 423 nd.psem_nameptr = nameptr; 424 nd.psem_namelen = plen; 425 nd.psem_hash = 0; 426 427 for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { 428 nd.psem_hash += (unsigned char)*cp * i; 429 } 430 431 /* 432 * attempt to allocate a new fp; if unsuccessful, the fp will be 433 * left unmodified (NULL). 434 */ 435 error = falloc(p, &fp, &indx, vfs_context_current()); 436 if (error) 437 goto bad; 438 439 /* 440 * We allocate a new entry if we are less than the maximum 441 * allowed and the one at the front of the LRU list is in use. 442 * Otherwise we use the one at the front of the LRU list. 443 */ 444 MALLOC(pcp, struct psemcache *, sizeof(struct psemcache), M_SHM, M_WAITOK|M_ZERO); 445 if (pcp == PSEMCACHE_NULL) { 446 error = ENOMEM; 447 goto bad; 448 } 449 450 MALLOC(new_pinfo, struct pseminfo *, sizeof(struct pseminfo), M_SHM, M_WAITOK|M_ZERO); 451 if (new_pinfo == NULL) { 452 error = ENOSPC; 453 goto bad; 454 } 455#if CONFIG_MACF 456 mac_posixsem_label_init(new_pinfo); 457#endif 458 459 /* 460 * Provisionally create the semaphore in the new_pinfo; we have to do 461 * this here to prevent locking later. We use the value of kret to 462 * signal success or failure, which is why we set its default value 463 * to KERN_INVALID_ADDRESS, above. 464 */ 465 466 fmode = FFLAGS(uap->oflag); 467 468 if((fmode & O_CREAT)) { 469 470 if((value < 0) || (value > SEM_VALUE_MAX)) { 471 error = EINVAL; 472 goto bad; 473 } 474 475 kret = semaphore_create(kernel_task, &new_pinfo->psem_semobject, SYNC_POLICY_FIFO, value); 476 477 if (kret != KERN_SUCCESS) { 478 switch (kret) { 479 case KERN_RESOURCE_SHORTAGE: 480 error = ENOMEM; 481 break; 482 case KERN_PROTECTION_FAILURE: 483 error = EACCES; 484 break; 485 default: 486 error = EINVAL; 487 } 488 goto bad; 489 } 490 } 491 492 MALLOC(new_pnode, struct psemnode *, sizeof(struct psemnode), M_SHM, M_WAITOK|M_ZERO); 493 if (new_pnode == NULL) { 494 error = ENOSPC; 495 goto bad; 496 } 497 498 PSEM_SUBSYS_LOCK(); 499 error = psem_cache_search(&pinfo, &nd, &pcache); 500 501 if (error == ENOENT) { 502 error = EINVAL; 503 goto bad_locked; 504 505 } 506 if (!error) { 507 incache = 0; 508 } else 509 incache = 1; 510 511 cmode &= ALLPERMS; 512 513 if (((fmode & (O_CREAT | O_EXCL))==(O_CREAT | O_EXCL)) && incache) { 514 /* sem exists and opened O_EXCL */ 515#if notyet 516 if (pinfo->psem_flags & PSEM_INDELETE) { 517 } 518#endif 519 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, 520 pinfo->psem_gid, pinfo->psem_mode); 521 error = EEXIST; 522 goto bad_locked; 523 } 524 if (((fmode & (O_CREAT | O_EXCL))== O_CREAT) && incache) { 525 /* As per POSIX, O_CREAT has no effect */ 526 fmode &= ~O_CREAT; 527 } 528 529 if ( (fmode & O_CREAT) ) { 530 /* create a new one (commit the allocation) */ 531 pinfo = new_pinfo; 532 pinfo->psem_flags = PSEM_DEFINED | PSEM_INCREATE; 533 pinfo->psem_usecount = 1; 534 pinfo->psem_mode = cmode; 535 pinfo->psem_uid = kauth_getuid(); 536 pinfo->psem_gid = kauth_getgid(); 537 bcopy(pnbuf, &pinfo->psem_name[0], PSEMNAMLEN); 538 pinfo->psem_name[PSEMNAMLEN]= 0; 539 pinfo->psem_flags &= ~PSEM_DEFINED; 540 pinfo->psem_flags |= PSEM_ALLOCATED; 541 pinfo->psem_creator_pid = p->p_pid; 542 pinfo->psem_creator_uniqueid = p->p_uniqueid; 543 544#if CONFIG_MACF 545 error = mac_posixsem_check_create(kauth_cred_get(), nameptr); 546 if (error) { 547 goto bad_locked; 548 } 549 mac_posixsem_label_associate(kauth_cred_get(), pinfo, nameptr); 550#endif 551 } else { 552 /* semaphore should exist as it is without O_CREAT */ 553 if (!incache) { 554 error = ENOENT; 555 goto bad_locked; 556 } 557 if( pinfo->psem_flags & PSEM_INDELETE) { 558 error = ENOENT; 559 goto bad_locked; 560 } 561 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, 562 pinfo->psem_gid, pinfo->psem_mode); 563#if CONFIG_MACF 564 error = mac_posixsem_check_open(kauth_cred_get(), pinfo); 565 if (error) { 566 goto bad_locked; 567 } 568#endif 569 if ( (error = psem_access(pinfo, fmode, kauth_cred_get())) ) { 570 goto bad_locked; 571 } 572 } 573 574 if (!incache) { 575 /* if successful, this will consume the pcp */ 576 if ( (error = psem_cache_add(pinfo, &nd, pcp)) ) { 577 goto bad_locked; 578 } 579 } 580 pinfo->psem_flags &= ~PSEM_INCREATE; 581 pinfo->psem_usecount++; 582 new_pnode->pinfo = pinfo; 583 PSEM_SUBSYS_UNLOCK(); 584 585 /* 586 * if incache, we did not use the new pcp or the new pcp or the 587 * new . and we must free them. 588 */ 589 if (incache) { 590 FREE(pcp, M_SHM); 591 pcp = PSEMCACHE_NULL; 592 if (new_pinfo != PSEMINFO_NULL) { 593 /* return value ignored - we can't _not_ do this */ 594 (void)semaphore_destroy(kernel_task, new_pinfo->psem_semobject); 595#if CONFIG_MACF 596 mac_posixsem_label_destroy(new_pinfo); 597#endif 598 FREE(new_pinfo, M_SHM); 599 new_pinfo = PSEMINFO_NULL; 600 } 601 } 602 603 proc_fdlock(p); 604 fp->f_flag = fmode & FMASK; 605 fp->f_ops = &psemops; 606 fp->f_data = (caddr_t)new_pnode; 607 procfdtbl_releasefd(p, indx, NULL); 608 fp_drop(p, indx, fp, 1); 609 proc_fdunlock(p); 610 611 *retval = CAST_USER_ADDR_T(indx); 612 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); 613 return (0); 614 615bad_locked: 616 PSEM_SUBSYS_UNLOCK(); 617bad: 618 if (pcp != PSEMCACHE_NULL) 619 FREE(pcp, M_SHM); 620 621 if (new_pnode != PSEMNODE_NULL) 622 FREE(new_pnode, M_SHM); 623 624 if (fp != NULL) 625 fp_free(p, indx, fp); 626 627 if (new_pinfo != PSEMINFO_NULL) { 628 /* 629 * kret signals whether or not we successfully created a 630 * Mach semaphore for this semaphore; if so, we need to 631 * destroy it here. 632 */ 633 if (kret == KERN_SUCCESS) { 634 /* return value ignored - we can't _not_ do this */ 635 (void)semaphore_destroy(kernel_task, new_pinfo->psem_semobject); 636 } 637#if CONFIG_MACF 638 mac_posixsem_label_destroy(new_pinfo); 639#endif 640 FREE(new_pinfo, M_SHM); 641 } 642 643 if (pnbuf != NULL) 644 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); 645 return (error); 646} 647 648/* 649 * XXX This code is repeated in several places 650 */ 651static int 652psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred) 653{ 654 int mode_req = ((mode & FREAD) ? S_IRUSR : 0) | 655 ((mode & FWRITE) ? S_IWUSR : 0); 656 657 /* Otherwise, user id 0 always gets access. */ 658 if (!suser(cred, NULL)) 659 return (0); 660 661 return(posix_cred_access(cred, pinfo->psem_uid, pinfo->psem_gid, pinfo->psem_mode, mode_req)); 662} 663 664int 665sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *retval) 666{ 667 size_t i; 668 int error=0; 669 struct psemname nd; 670 struct pseminfo *pinfo; 671 char * pnbuf; 672 char * nameptr; 673 char * cp; 674 size_t pathlen, plen; 675 int incache = 0; 676 struct psemcache *pcache = PSEMCACHE_NULL; 677 678 pinfo = PSEMINFO_NULL; 679 680 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 681 if (pnbuf == NULL) { 682 return(ENOSPC); /* XXX non-standard */ 683 } 684 pathlen = MAXPATHLEN; 685 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); 686 if (error) { 687 goto bad; 688 } 689 AUDIT_ARG(text, pnbuf); 690 if (pathlen > PSEMNAMLEN) { 691 error = ENAMETOOLONG; 692 goto bad; 693 } 694 695 696#ifdef PSXSEM_NAME_RESTRICT 697 nameptr = pnbuf; 698 if (*nameptr == '/') { 699 while (*(nameptr++) == '/') { 700 plen--; 701 error = EINVAL; 702 goto bad; 703 } 704 } else { 705 error = EINVAL; 706 goto bad; 707 } 708#endif /* PSXSEM_NAME_RESTRICT */ 709 710 plen = pathlen; 711 nameptr = pnbuf; 712 nd.psem_nameptr = nameptr; 713 nd.psem_namelen = plen; 714 nd. psem_hash =0; 715 716 for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { 717 nd.psem_hash += (unsigned char)*cp * i; 718 } 719 720 PSEM_SUBSYS_LOCK(); 721 error = psem_cache_search(&pinfo, &nd, &pcache); 722 723 if (error == ENOENT) { 724 PSEM_SUBSYS_UNLOCK(); 725 error = EINVAL; 726 goto bad; 727 728 } 729 if (!error) { 730 PSEM_SUBSYS_UNLOCK(); 731 error = EINVAL; 732 goto bad; 733 } else 734 incache = 1; 735#if CONFIG_MACF 736 error = mac_posixsem_check_unlink(kauth_cred_get(), pinfo, nameptr); 737 if (error) { 738 PSEM_SUBSYS_UNLOCK(); 739 goto bad; 740 } 741#endif 742 if ( (error = psem_access(pinfo, pinfo->psem_mode, kauth_cred_get())) ) { 743 PSEM_SUBSYS_UNLOCK(); 744 goto bad; 745 } 746 747 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) { 748 PSEM_SUBSYS_UNLOCK(); 749 error = EINVAL; 750 goto bad; 751 } 752 753 if ( (pinfo->psem_flags & PSEM_INDELETE) ) { 754 PSEM_SUBSYS_UNLOCK(); 755 error = 0; 756 goto bad; 757 } 758 759 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid, 760 pinfo->psem_mode); 761 762 pinfo->psem_flags |= PSEM_INDELETE; 763 pinfo->psem_usecount--; 764 765 if (!pinfo->psem_usecount) { 766 psem_delete(pinfo); 767 FREE(pinfo,M_SHM); 768 } else 769 pinfo->psem_flags |= PSEM_REMOVED; 770 771 psem_cache_delete(pcache); 772 PSEM_SUBSYS_UNLOCK(); 773 FREE(pcache, M_SHM); 774 error = 0; 775bad: 776 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); 777 return (error); 778} 779 780int 781sem_close(proc_t p, struct sem_close_args *uap, __unused int32_t *retval) 782{ 783 int fd = CAST_DOWN_EXPLICIT(int,uap->sem); 784 struct fileproc *fp; 785 int error = 0; 786 787 AUDIT_ARG(fd, fd); /* XXX This seems wrong; uap->sem is a pointer */ 788 789 proc_fdlock(p); 790 error = fp_lookup(p,fd, &fp, 1); 791 if (error) { 792 proc_fdunlock(p); 793 return(error); 794 } 795 procfdtbl_markclosefd(p, fd); 796 fileproc_drain(p, fp); 797 fdrelse(p, fd); 798 error = closef_locked(fp, fp->f_fglob, p); 799 fileproc_free(fp); 800 proc_fdunlock(p); 801 return(error); 802} 803 804int 805sem_wait(proc_t p, struct sem_wait_args *uap, int32_t *retval) 806{ 807 __pthread_testcancel(1); 808 return(sem_wait_nocancel(p, (struct sem_wait_nocancel_args *)uap, retval)); 809} 810 811int 812sem_wait_nocancel(proc_t p, struct sem_wait_nocancel_args *uap, __unused int32_t *retval) 813{ 814 int fd = CAST_DOWN_EXPLICIT(int,uap->sem); 815 struct fileproc *fp; 816 struct pseminfo * pinfo; 817 struct psemnode * pnode ; 818 kern_return_t kret; 819 int error; 820 821 error = fp_getfpsem(p, fd, &fp, &pnode); 822 if (error) 823 return (error); 824 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { 825 error = EINVAL; 826 goto out; 827 } 828 PSEM_SUBSYS_LOCK(); 829 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { 830 PSEM_SUBSYS_UNLOCK(); 831 error = EINVAL; 832 goto out; 833 } 834 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) 835 != PSEM_ALLOCATED) { 836 PSEM_SUBSYS_UNLOCK(); 837 error = EINVAL; 838 goto out; 839 } 840#if CONFIG_MACF 841 error = mac_posixsem_check_wait(kauth_cred_get(), pinfo); 842 if (error) { 843 PSEM_SUBSYS_UNLOCK(); 844 goto out; 845 } 846#endif 847 PSEM_SUBSYS_UNLOCK(); 848 kret = semaphore_wait(pinfo->psem_semobject); 849 switch (kret) { 850 case KERN_INVALID_ADDRESS: 851 case KERN_PROTECTION_FAILURE: 852 error = EACCES; 853 break; 854 case KERN_ABORTED: 855 case KERN_OPERATION_TIMED_OUT: 856 error = EINTR; 857 break; 858 case KERN_SUCCESS: 859 error = 0; 860 break; 861 default: 862 error = EINVAL; 863 break; 864 } 865out: 866 fp_drop(p, fd, fp, 0); 867 return(error); 868 869} 870 871int 872sem_trywait(proc_t p, struct sem_trywait_args *uap, __unused int32_t *retval) 873{ 874 int fd = CAST_DOWN_EXPLICIT(int,uap->sem); 875 struct fileproc *fp; 876 struct pseminfo * pinfo; 877 struct psemnode * pnode ; 878 kern_return_t kret; 879 mach_timespec_t wait_time; 880 int error; 881 882 error = fp_getfpsem(p, fd, &fp, &pnode); 883 if (error) 884 return (error); 885 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { 886 error = EINVAL; 887 goto out; 888 } 889 PSEM_SUBSYS_LOCK(); 890 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { 891 PSEM_SUBSYS_UNLOCK(); 892 error = EINVAL; 893 goto out; 894 } 895 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) 896 != PSEM_ALLOCATED) { 897 PSEM_SUBSYS_UNLOCK(); 898 error = EINVAL; 899 goto out; 900 } 901#if CONFIG_MACF 902 error = mac_posixsem_check_wait(kauth_cred_get(), pinfo); 903 if (error) { 904 PSEM_SUBSYS_UNLOCK(); 905 goto out; 906 } 907#endif 908 PSEM_SUBSYS_UNLOCK(); 909 wait_time.tv_sec = 0; 910 wait_time.tv_nsec = 0; 911 912 kret = semaphore_timedwait(pinfo->psem_semobject, MACH_TIMESPEC_ZERO); 913 switch (kret) { 914 case KERN_INVALID_ADDRESS: 915 case KERN_PROTECTION_FAILURE: 916 error = EINVAL; 917 break; 918 case KERN_ABORTED: 919 error = EINTR; 920 break; 921 case KERN_OPERATION_TIMED_OUT: 922 error = EAGAIN; 923 break; 924 case KERN_SUCCESS: 925 error = 0; 926 break; 927 default: 928 error = EINVAL; 929 break; 930 } 931out: 932 fp_drop(p, fd, fp, 0); 933 return(error); 934} 935 936int 937sem_post(proc_t p, struct sem_post_args *uap, __unused int32_t *retval) 938{ 939 int fd = CAST_DOWN_EXPLICIT(int,uap->sem); 940 struct fileproc *fp; 941 struct pseminfo * pinfo; 942 struct psemnode * pnode ; 943 kern_return_t kret; 944 int error; 945 946 error = fp_getfpsem(p, fd, &fp, &pnode); 947 if (error) 948 return (error); 949 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { 950 error = EINVAL; 951 goto out; 952 } 953 PSEM_SUBSYS_LOCK(); 954 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { 955 PSEM_SUBSYS_UNLOCK(); 956 error = EINVAL; 957 goto out; 958 } 959 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) 960 != PSEM_ALLOCATED) { 961 PSEM_SUBSYS_UNLOCK(); 962 error = EINVAL; 963 goto out; 964 } 965#if CONFIG_MACF 966 error = mac_posixsem_check_post(kauth_cred_get(), pinfo); 967 if (error) { 968 PSEM_SUBSYS_UNLOCK(); 969 goto out; 970 } 971#endif 972 PSEM_SUBSYS_UNLOCK(); 973 kret = semaphore_signal(pinfo->psem_semobject); 974 switch (kret) { 975 case KERN_INVALID_ADDRESS: 976 case KERN_PROTECTION_FAILURE: 977 error = EINVAL; 978 break; 979 case KERN_ABORTED: 980 case KERN_OPERATION_TIMED_OUT: 981 error = EINTR; 982 break; 983 case KERN_SUCCESS: 984 error = 0; 985 break; 986 default: 987 error = EINVAL; 988 break; 989 } 990out: 991 fp_drop(p, fd, fp, 0); 992 return(error); 993} 994 995int 996sem_init(__unused proc_t p, __unused struct sem_init_args *uap, __unused int32_t *retval) 997{ 998 return(ENOSYS); 999} 1000 1001int 1002sem_destroy(__unused proc_t p, __unused struct sem_destroy_args *uap, __unused int32_t *retval) 1003{ 1004 return(ENOSYS); 1005} 1006 1007int 1008sem_getvalue(__unused proc_t p, __unused struct sem_getvalue_args *uap, __unused int32_t *retval) 1009{ 1010 return(ENOSYS); 1011} 1012 1013static int 1014psem_close(struct psemnode *pnode, __unused int flags) 1015{ 1016 int error=0; 1017 struct pseminfo *pinfo; 1018 1019 PSEM_SUBSYS_LOCK(); 1020 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL){ 1021 PSEM_SUBSYS_UNLOCK(); 1022 return(EINVAL); 1023 } 1024 1025 if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) { 1026 PSEM_SUBSYS_UNLOCK(); 1027 return(EINVAL); 1028 } 1029#if DIAGNOSTIC 1030 if(!pinfo->psem_usecount) { 1031 kprintf("negative usecount in psem_close\n"); 1032 } 1033#endif /* DIAGNOSTIC */ 1034 pinfo->psem_usecount--; 1035 1036 if ((pinfo->psem_flags & PSEM_REMOVED) && !pinfo->psem_usecount) { 1037 PSEM_SUBSYS_UNLOCK(); 1038 /* lock dropped as only semaphore is destroyed here */ 1039 error = psem_delete(pinfo); 1040 FREE(pinfo,M_SHM); 1041 } else { 1042 PSEM_SUBSYS_UNLOCK(); 1043 } 1044 /* subsystem lock is dropped when we get here */ 1045 FREE(pnode, M_SHM); 1046 return (error); 1047} 1048 1049static int 1050psem_closefile(struct fileglob *fg, __unused vfs_context_t ctx) 1051{ 1052 int error; 1053 1054 /* 1055 * Not locked as psem_close is called only from here and is locked 1056 * properly 1057 */ 1058 error = psem_close(((struct psemnode *)fg->fg_data), fg->fg_flag); 1059 1060 return(error); 1061} 1062 1063static int 1064psem_delete(struct pseminfo * pinfo) 1065{ 1066 kern_return_t kret; 1067 1068 kret = semaphore_destroy(kernel_task, pinfo->psem_semobject); 1069#if CONFIG_MACF 1070 mac_posixsem_label_destroy(pinfo); 1071#endif 1072 1073 switch (kret) { 1074 case KERN_INVALID_ADDRESS: 1075 case KERN_PROTECTION_FAILURE: 1076 return (EINVAL); 1077 case KERN_ABORTED: 1078 case KERN_OPERATION_TIMED_OUT: 1079 return (EINTR); 1080 case KERN_SUCCESS: 1081 return(0); 1082 default: 1083 return (EINVAL); 1084 } 1085} 1086 1087static int 1088psem_read(__unused struct fileproc *fp, __unused struct uio *uio, 1089 __unused int flags, __unused vfs_context_t ctx) 1090{ 1091 return(ENOTSUP); 1092} 1093 1094static int 1095psem_write(__unused struct fileproc *fp, __unused struct uio *uio, 1096 __unused int flags, __unused vfs_context_t ctx) 1097{ 1098 return(ENOTSUP); 1099} 1100 1101static int 1102psem_ioctl(__unused struct fileproc *fp, __unused u_long com, 1103 __unused caddr_t data, __unused vfs_context_t ctx) 1104{ 1105 return(ENOTSUP); 1106} 1107 1108static int 1109psem_select(__unused struct fileproc *fp, __unused int which, 1110 __unused void *wql, __unused vfs_context_t ctx) 1111{ 1112 return(ENOTSUP); 1113} 1114 1115static int 1116psem_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn, 1117 __unused vfs_context_t ctx) 1118{ 1119 return (ENOTSUP); 1120} 1121 1122int 1123fill_pseminfo(struct psemnode *pnode, struct psem_info * info) 1124{ 1125 struct pseminfo *pinfo; 1126 struct vinfo_stat *sb; 1127 1128 PSEM_SUBSYS_LOCK(); 1129 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL){ 1130 PSEM_SUBSYS_UNLOCK(); 1131 return(EINVAL); 1132 } 1133 1134#if 0 1135 if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) { 1136 PSEM_SUBSYS_UNLOCK(); 1137 return(EINVAL); 1138 } 1139#endif 1140 1141 sb = &info->psem_stat; 1142 bzero(sb, sizeof(struct vinfo_stat)); 1143 1144 sb->vst_mode = pinfo->psem_mode; 1145 sb->vst_uid = pinfo->psem_uid; 1146 sb->vst_gid = pinfo->psem_gid; 1147 sb->vst_size = pinfo->psem_usecount; 1148 bcopy(&pinfo->psem_name[0], &info->psem_name[0], PSEMNAMLEN+1); 1149 1150 PSEM_SUBSYS_UNLOCK(); 1151 return(0); 1152} 1153 1154#if CONFIG_MACF 1155void 1156psem_label_associate(struct fileproc *fp, struct vnode *vp, vfs_context_t ctx) 1157{ 1158 struct psemnode *pnode; 1159 struct pseminfo *psem; 1160 1161 PSEM_SUBSYS_LOCK(); 1162 pnode = (struct psemnode *)fp->f_fglob->fg_data; 1163 if (pnode != NULL) { 1164 psem = pnode->pinfo; 1165 if (psem != NULL) 1166 mac_posixsem_vnode_label_associate( 1167 vfs_context_ucred(ctx), psem, psem->psem_label, 1168 vp, vp->v_label); 1169 } 1170 PSEM_SUBSYS_UNLOCK(); 1171} 1172#endif 1173 1174