1 /* 2 * Copyright (C) 2005 David Xu <davidxu@freebsd.org>. 3 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice(s), this list of conditions and the following disclaimer as 11 * the first lines of this file unmodified other than the possible 12 * addition of one or more copyright notices. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice(s), this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "namespace.h" 32 #include <machine/tls.h> 33 #include <sys/semaphore.h> 34 #include <sys/mman.h> 35 #include <sys/queue.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <limits.h> 42 #include <pthread.h> 43 #include <stdarg.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <time.h> 47 #include <unistd.h> 48 #include "un-namespace.h" 49 #include "thr_private.h" 50 51 #define container_of(ptr, type, member) \ 52 ({ \ 53 __typeof(((type *)0)->member) *_p = (ptr); \ 54 (type *)((char *)_p - offsetof(type, member)); \ 55 }) 56 57 /* 58 * Semaphore definitions. 59 */ 60 struct sem { 61 u_int32_t magic; 62 volatile umtx_t count; 63 int semid; 64 int unused; /* pad */ 65 }; 66 67 #define SEM_MAGIC ((u_int32_t) 0x09fa4012) 68 69 static char const *sem_prefix = "/var/run/sem"; 70 71 72 /* 73 * POSIX requires that two successive calls to sem_open return 74 * the same address if no call to unlink nor close have been 75 * done in the middle. For that, we keep a list of open semaphore 76 * and search for an existing one before remapping a semaphore. 77 * We have to keep the fd open to check for races. 78 * 79 * Example : 80 * sem_open("/test", O_CREAT | O_EXCL...) -> fork() -> 81 * parent : 82 * sem_unlink("/test") -> sem_open("/test", O_CREAT | O_EXCl ...) 83 * child : 84 * sem_open("/test", 0). 85 * We need to check that the cached mapping is the one of the most up 86 * to date file linked at this name, or child process will reopen the 87 * *old* version of the semaphore, which is wrong. 88 * 89 * fstat and nlink check is used to test for this race. 90 */ 91 92 struct sem_info { 93 int open_count; 94 ino_t inode; 95 dev_t dev; 96 int fd; 97 sem_t sem; 98 LIST_ENTRY(sem_info) next; 99 }; 100 101 102 103 static pthread_once_t once = PTHREAD_ONCE_INIT; 104 static pthread_mutex_t sem_lock; 105 static LIST_HEAD(,sem_info) sem_list = LIST_HEAD_INITIALIZER(sem_list); 106 107 108 #define SEMID_LWP 0 109 #define SEMID_FORK 1 110 #define SEMID_NAMED 2 111 112 static void 113 sem_prefork(void) 114 { 115 _pthread_mutex_lock(&sem_lock); 116 } 117 118 static void 119 sem_postfork(void) 120 { 121 _pthread_mutex_unlock(&sem_lock); 122 } 123 124 static void 125 sem_child_postfork(void) 126 { 127 _pthread_mutex_unlock(&sem_lock); 128 } 129 130 static void 131 sem_module_init(void) 132 { 133 pthread_mutexattr_t ma; 134 135 _pthread_mutexattr_init(&ma); 136 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 137 _pthread_mutex_init(&sem_lock, &ma); 138 _pthread_mutexattr_destroy(&ma); 139 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 140 } 141 142 static inline int 143 sem_check_validity(sem_t *sem) 144 { 145 146 if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) { 147 return (0); 148 } else { 149 errno = EINVAL; 150 return (-1); 151 } 152 } 153 154 static sem_t 155 sem_alloc(unsigned int value, int pshared) 156 { 157 sem_t sem; 158 int semid; 159 160 if (value > SEM_VALUE_MAX) { 161 errno = EINVAL; 162 return (NULL); 163 } 164 if (pshared) { 165 static __thread sem_t sem_base; 166 static __thread int sem_count; 167 168 if (sem_base == NULL) { 169 sem_base = mmap(NULL, getpagesize(), 170 PROT_READ | PROT_WRITE, 171 MAP_ANON | MAP_SHARED, 172 -1, 0); 173 sem_count = getpagesize() / sizeof(*sem); 174 } 175 sem = sem_base++; 176 if (--sem_count == 0) 177 sem_base = NULL; 178 semid = SEMID_FORK; 179 } else { 180 sem = malloc(sizeof(struct sem)); 181 semid = SEMID_LWP; 182 } 183 if (sem == NULL) { 184 errno = ENOSPC; 185 return (NULL); 186 } 187 sem->magic = SEM_MAGIC; 188 sem->count = (u_int32_t)value; 189 sem->semid = semid; 190 return (sem); 191 } 192 193 int 194 _sem_init(sem_t *sem, int pshared, unsigned int value) 195 { 196 if (sem == NULL) { 197 errno = EINVAL; 198 return (-1); 199 } 200 201 *sem = sem_alloc(value, pshared); 202 if (*sem == NULL) 203 return (-1); 204 return (0); 205 } 206 207 int 208 _sem_destroy(sem_t *sem) 209 { 210 if (sem_check_validity(sem) != 0) { 211 errno = EINVAL; 212 return (-1); 213 } 214 215 (*sem)->magic = 0; 216 217 switch ((*sem)->semid) { 218 case SEMID_LWP: 219 free(*sem); 220 break; 221 case SEMID_FORK: 222 /* memory is left intact */ 223 break; 224 default: 225 errno = EINVAL; 226 return (-1); 227 } 228 return (0); 229 } 230 231 int 232 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 233 { 234 if (sem_check_validity(sem) != 0) 235 return (-1); 236 237 *sval = (*sem)->count; 238 return (0); 239 } 240 241 int 242 _sem_trywait(sem_t *sem) 243 { 244 int val; 245 246 if (sem_check_validity(sem) != 0) 247 return (-1); 248 249 while ((val = (*sem)->count) > 0) { 250 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) 251 return (0); 252 } 253 errno = EAGAIN; 254 return (-1); 255 } 256 257 int 258 _sem_wait(sem_t *sem) 259 { 260 struct pthread *curthread; 261 int val, oldcancel, retval; 262 263 if (sem_check_validity(sem) != 0) 264 return (-1); 265 266 curthread = tls_get_curthread(); 267 _pthread_testcancel(); 268 do { 269 while ((val = (*sem)->count) > 0) { 270 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 271 return (0); 272 } 273 oldcancel = _thr_cancel_enter(curthread); 274 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0); 275 _thr_cancel_leave(curthread, oldcancel); 276 } while (retval == 0); 277 errno = retval; 278 return (-1); 279 } 280 281 int 282 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime) 283 { 284 struct timespec ts, ts2; 285 struct pthread *curthread; 286 int val, oldcancel, retval; 287 288 if (sem_check_validity(sem) != 0) 289 return (-1); 290 291 curthread = tls_get_curthread(); 292 293 /* 294 * The timeout argument is only supposed to 295 * be checked if the thread would have blocked. 296 */ 297 _pthread_testcancel(); 298 do { 299 while ((val = (*sem)->count) > 0) { 300 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 301 return (0); 302 } 303 if (abstime == NULL || 304 abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 305 errno = EINVAL; 306 return (-1); 307 } 308 clock_gettime(CLOCK_REALTIME, &ts); 309 TIMESPEC_SUB(&ts2, abstime, &ts); 310 oldcancel = _thr_cancel_enter(curthread); 311 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2, 312 CLOCK_REALTIME); 313 _thr_cancel_leave(curthread, oldcancel); 314 } while (retval == 0); 315 errno = retval; 316 return (-1); 317 } 318 319 int 320 _sem_post(sem_t *sem) 321 { 322 int val; 323 324 if (sem_check_validity(sem) != 0) 325 return (-1); 326 327 /* 328 * sem_post() is required to be safe to call from within 329 * signal handlers, these code should work as that. 330 */ 331 do { 332 val = (*sem)->count; 333 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 334 _thr_umtx_wake(&(*sem)->count, val + 1); 335 return (0); 336 } 337 338 static int 339 get_path(const char *name, char *path, size_t len, char const **prefix) 340 { 341 size_t path_len; 342 343 *prefix = NULL; 344 345 if (name[0] == '/') { 346 *prefix = getenv("LIBTHREAD_SEM_PREFIX"); 347 348 if (*prefix == NULL) 349 *prefix = sem_prefix; 350 351 path_len = strlcpy(path, *prefix, len); 352 353 if (path_len > len) { 354 return (ENAMETOOLONG); 355 } 356 } 357 358 path_len = strlcat(path, name, len); 359 360 if (path_len > len) 361 return (ENAMETOOLONG); 362 363 return (0); 364 } 365 366 367 static sem_t * 368 sem_get_mapping(ino_t inode, dev_t dev) 369 { 370 struct sem_info *ni; 371 struct stat sbuf; 372 373 LIST_FOREACH(ni, &sem_list, next) { 374 if (ni->inode == inode && ni->dev == dev) { 375 /* Check for races */ 376 if(_fstat(ni->fd, &sbuf) == 0) { 377 if (sbuf.st_nlink > 0) { 378 ni->open_count++; 379 return (&ni->sem); 380 } else { 381 ni->inode = 0; 382 LIST_REMOVE(ni, next); 383 } 384 } 385 return (SEM_FAILED); 386 387 } 388 } 389 390 return (SEM_FAILED); 391 } 392 393 394 static sem_t * 395 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd) 396 { 397 struct sem_info *ni; 398 399 ni = malloc(sizeof(struct sem_info)); 400 if (ni == NULL) { 401 errno = ENOSPC; 402 return (SEM_FAILED); 403 } 404 405 bzero(ni, sizeof(*ni)); 406 ni->open_count = 1; 407 ni->sem = sem; 408 ni->fd = fd; 409 ni->inode = inode; 410 ni->dev = dev; 411 412 LIST_INSERT_HEAD(&sem_list, ni, next); 413 414 return (&ni->sem); 415 } 416 417 static int 418 sem_close_mapping(sem_t *sem) 419 { 420 struct sem_info *ni; 421 422 if ((*sem)->semid != SEMID_NAMED) 423 return (EINVAL); 424 425 ni = container_of(sem, struct sem_info, sem); 426 427 if ( --ni->open_count > 0) { 428 return (0); 429 } else { 430 if (ni->inode != 0) { 431 LIST_REMOVE(ni, next); 432 } 433 munmap(ni->sem, getpagesize()); 434 __sys_close(ni->fd); 435 free(ni); 436 return (0); 437 } 438 } 439 440 sem_t * 441 _sem_open(const char *name, int oflag, ...) 442 { 443 char path[PATH_MAX]; 444 char tmppath[PATH_MAX]; 445 char const *prefix = NULL; 446 char *tmpname = NULL; 447 size_t path_len; 448 int error, fd, create; 449 sem_t *sem; 450 sem_t semtmp; 451 va_list ap; 452 mode_t mode; 453 struct stat sbuf; 454 unsigned int value = 0; 455 unsigned int retry_count; 456 457 create = 0; 458 error = 0; 459 fd = -1; 460 sem = SEM_FAILED; 461 462 /* 463 * Bail out if invalid flags specified. 464 */ 465 if (oflag & ~(O_CREAT|O_EXCL)) { 466 errno = EINVAL; 467 return (SEM_FAILED); 468 } 469 470 oflag |= O_RDWR; 471 oflag |= O_CLOEXEC; 472 473 if (name == NULL) { 474 errno = EINVAL; 475 return (SEM_FAILED); 476 } 477 478 _pthread_once(&once, sem_module_init); 479 480 _pthread_mutex_lock(&sem_lock); 481 482 error = get_path(name, path, PATH_MAX, &prefix); 483 if (error) { 484 errno = error; 485 goto error; 486 } 487 488 retry: 489 tmpname = NULL; 490 retry_count = 10; 491 492 fd = __sys_open(path, O_RDWR | O_CLOEXEC); 493 494 if (fd > 0) { 495 496 if ((oflag & O_EXCL) == O_EXCL) { 497 __sys_close(fd); 498 errno = EEXIST; 499 goto error; 500 } 501 502 if (_fstat(fd, &sbuf) != 0) { 503 /* Bad things happened, like another thread closing our descriptor */ 504 __sys_close(fd); 505 errno = EINVAL; 506 goto error; 507 } 508 509 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev); 510 511 if (sem != SEM_FAILED) { 512 __sys_close(fd); 513 goto done; 514 } 515 516 if ((sbuf.st_mode & S_IFREG) == 0) { 517 /* We only want regular files here */ 518 __sys_close(fd); 519 errno = EINVAL; 520 goto error; 521 } 522 } else if ((oflag & O_CREAT) && errno == ENOENT) { 523 524 va_start(ap, oflag); 525 526 mode = (mode_t) va_arg(ap, int); 527 value = (unsigned int) va_arg(ap, int); 528 529 va_end(ap); 530 531 if (value > SEM_VALUE_MAX) { 532 errno = EINVAL; 533 goto error; 534 } 535 536 strlcpy(tmppath, prefix, sizeof(tmppath)); 537 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath)); 538 539 if (path_len > sizeof(tmppath)) { 540 errno = ENAMETOOLONG; 541 goto error; 542 } 543 544 while (retry_count-- > 0) { 545 tmpname = mktemp(tmppath); 546 547 if ( tmpname == NULL) { 548 errno = EINVAL; 549 goto error; 550 } 551 552 fd = __sys_open(tmpname, O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, mode); 553 554 if (fd > 0 || errno != EEXIST) { 555 break; 556 } 557 558 } 559 560 if (retry_count == 0) { 561 __sys_close(fd); 562 errno = ENOSPC; /* XXX POSIX does not allow for EAGAIN */ 563 goto error; 564 } 565 566 create = 1; 567 } 568 569 if (fd == -1) { 570 switch (errno) { 571 case ENOTDIR: 572 case EISDIR: 573 case EMLINK: 574 case ELOOP: 575 errno = EINVAL; 576 break; 577 case EDQUOT: 578 case EIO: 579 errno = ENOSPC; 580 break; 581 case EROFS: 582 errno = EACCES; 583 } 584 goto error; 585 } 586 587 semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, 588 MAP_NOSYNC | MAP_SHARED, fd, 0); 589 590 if (semtmp == MAP_FAILED) { 591 if (errno != EACCES && errno != EMFILE) 592 errno = ENOMEM; 593 594 if (create) 595 unlink(tmpname); 596 597 __sys_close(fd); 598 goto error; 599 } 600 601 if (create) { 602 ftruncate(fd, sizeof(struct sem)); 603 semtmp->magic = SEM_MAGIC; 604 semtmp->count = (u_int32_t)value; 605 semtmp->semid = SEMID_NAMED; 606 607 if (link(tmpname, path) != 0) { 608 munmap(semtmp, getpagesize()); 609 __sys_close(fd); 610 unlink(tmpname); 611 612 if (errno == EEXIST && (oflag & O_EXCL) == 0) { 613 goto retry; 614 } 615 616 goto error; 617 } 618 unlink(tmpname); 619 620 if (_fstat(fd, &sbuf) != 0) { 621 /* Bad things happened, like another thread closing our descriptor */ 622 munmap(semtmp, getpagesize()); 623 __sys_close(fd); 624 errno = EINVAL; 625 goto error; 626 } 627 628 } 629 sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd); 630 631 done: 632 _pthread_mutex_unlock(&sem_lock); 633 return (sem); 634 635 error: 636 _pthread_mutex_unlock(&sem_lock); 637 return (SEM_FAILED); 638 639 } 640 641 int 642 _sem_close(sem_t *sem) 643 { 644 _pthread_once(&once, sem_module_init); 645 646 _pthread_mutex_lock(&sem_lock); 647 648 if (sem_check_validity(sem)) { 649 _pthread_mutex_unlock(&sem_lock); 650 errno = EINVAL; 651 return (-1); 652 } 653 654 if (sem_close_mapping(sem)) { 655 _pthread_mutex_unlock(&sem_lock); 656 errno = EINVAL; 657 return (-1); 658 } 659 _pthread_mutex_unlock(&sem_lock); 660 661 return (0); 662 } 663 664 int 665 _sem_unlink(const char *name) 666 { 667 char path[PATH_MAX]; 668 const char *prefix; 669 int error; 670 671 error = get_path(name, path, PATH_MAX, &prefix); 672 if (error) { 673 errno = error; 674 return (-1); 675 } 676 677 error = unlink(path); 678 679 if(error) { 680 if (errno != ENAMETOOLONG && errno != ENOENT) 681 errno = EACCES; 682 683 return (-1); 684 } 685 686 return (0); 687 } 688 689 __strong_reference(_sem_destroy, sem_destroy); 690 __strong_reference(_sem_getvalue, sem_getvalue); 691 __strong_reference(_sem_init, sem_init); 692 __strong_reference(_sem_trywait, sem_trywait); 693 __strong_reference(_sem_wait, sem_wait); 694 __strong_reference(_sem_timedwait, sem_timedwait); 695 __strong_reference(_sem_post, sem_post); 696 __strong_reference(_sem_open, sem_open); 697 __strong_reference(_sem_close, sem_close); 698 __strong_reference(_sem_unlink, sem_unlink); 699 700