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 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <pthread.h> 42 #include <stdarg.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <time.h> 46 #include <unistd.h> 47 #include "un-namespace.h" 48 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 _thr_atfork_kern(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 errno = EINVAL; 236 return (-1); 237 } 238 239 *sval = (*sem)->count; 240 return (0); 241 } 242 243 int 244 _sem_trywait(sem_t *sem) 245 { 246 int val; 247 248 if (sem_check_validity(sem) != 0) { 249 errno = EINVAL; 250 return (-1); 251 } 252 253 while ((val = (*sem)->count) > 0) { 254 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) 255 return (0); 256 } 257 errno = EAGAIN; 258 return (-1); 259 } 260 261 int 262 _sem_wait(sem_t *sem) 263 { 264 struct pthread *curthread; 265 int val, oldcancel, retval; 266 267 if (sem_check_validity(sem) != 0) { 268 errno = EINVAL; 269 return (-1); 270 } 271 272 curthread = tls_get_curthread(); 273 _pthread_testcancel(); 274 275 for (;;) { 276 while ((val = (*sem)->count) > 0) { 277 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 278 return (0); 279 } 280 oldcancel = _thr_cancel_enter(curthread); 281 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0); 282 _thr_cancel_leave(curthread, oldcancel); 283 /* ignore retval */ 284 } 285 286 errno = retval; 287 288 return (-1); 289 } 290 291 int 292 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime) 293 { 294 struct timespec ts, ts2; 295 struct pthread *curthread; 296 int val, oldcancel, retval; 297 298 if (sem_check_validity(sem) != 0) 299 return (-1); 300 301 curthread = tls_get_curthread(); 302 _pthread_testcancel(); 303 304 /* 305 * The timeout argument is only supposed to 306 * be checked if the thread would have blocked. 307 */ 308 do { 309 while ((val = (*sem)->count) > 0) { 310 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 311 return (0); 312 } 313 if (abstime == NULL || 314 abstime->tv_nsec >= 1000000000 || 315 abstime->tv_nsec < 0) { 316 errno = EINVAL; 317 return (-1); 318 } 319 clock_gettime(CLOCK_REALTIME, &ts); 320 TIMESPEC_SUB(&ts2, abstime, &ts); 321 oldcancel = _thr_cancel_enter(curthread); 322 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2, 323 CLOCK_REALTIME); 324 _thr_cancel_leave(curthread, oldcancel); 325 } while (retval != ETIMEDOUT && retval != EINTR); 326 327 errno = retval; 328 329 return (-1); 330 } 331 332 int 333 _sem_post(sem_t *sem) 334 { 335 int val; 336 337 if (sem_check_validity(sem) != 0) 338 return (-1); 339 340 /* 341 * sem_post() is required to be safe to call from within 342 * signal handlers, these code should work as that. 343 */ 344 do { 345 val = (*sem)->count; 346 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1)); 347 _thr_umtx_wake(&(*sem)->count, val + 1); 348 return (0); 349 } 350 351 static int 352 get_path(const char *name, char *path, size_t len, char const **prefix) 353 { 354 size_t path_len; 355 356 *prefix = NULL; 357 358 if (name[0] == '/') { 359 *prefix = getenv("LIBTHREAD_SEM_PREFIX"); 360 361 if (*prefix == NULL) 362 *prefix = sem_prefix; 363 364 path_len = strlcpy(path, *prefix, len); 365 366 if (path_len > len) { 367 return (ENAMETOOLONG); 368 } 369 } 370 371 path_len = strlcat(path, name, len); 372 373 if (path_len > len) 374 return (ENAMETOOLONG); 375 376 return (0); 377 } 378 379 380 static sem_t * 381 sem_get_mapping(ino_t inode, dev_t dev) 382 { 383 struct sem_info *ni; 384 struct stat sbuf; 385 386 LIST_FOREACH(ni, &sem_list, next) { 387 if (ni->inode == inode && ni->dev == dev) { 388 /* Check for races */ 389 if(_fstat(ni->fd, &sbuf) == 0) { 390 if (sbuf.st_nlink > 0) { 391 ni->open_count++; 392 return (&ni->sem); 393 } else { 394 ni->inode = 0; 395 LIST_REMOVE(ni, next); 396 } 397 } 398 return (SEM_FAILED); 399 400 } 401 } 402 403 return (SEM_FAILED); 404 } 405 406 407 static sem_t * 408 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd) 409 { 410 struct sem_info *ni; 411 412 ni = malloc(sizeof(struct sem_info)); 413 if (ni == NULL) { 414 errno = ENOSPC; 415 return (SEM_FAILED); 416 } 417 418 bzero(ni, sizeof(*ni)); 419 ni->open_count = 1; 420 ni->sem = sem; 421 ni->fd = fd; 422 ni->inode = inode; 423 ni->dev = dev; 424 425 LIST_INSERT_HEAD(&sem_list, ni, next); 426 427 return (&ni->sem); 428 } 429 430 static int 431 sem_close_mapping(sem_t *sem) 432 { 433 struct sem_info *ni; 434 435 if ((*sem)->semid != SEMID_NAMED) 436 return (EINVAL); 437 438 ni = container_of(sem, struct sem_info, sem); 439 440 if ( --ni->open_count > 0) { 441 return (0); 442 } else { 443 if (ni->inode != 0) { 444 LIST_REMOVE(ni, next); 445 } 446 munmap(ni->sem, getpagesize()); 447 __sys_close(ni->fd); 448 free(ni); 449 return (0); 450 } 451 } 452 453 sem_t * 454 _sem_open(const char *name, int oflag, ...) 455 { 456 char path[PATH_MAX]; 457 char tmppath[PATH_MAX]; 458 char const *prefix = NULL; 459 size_t path_len; 460 int error, fd, create; 461 sem_t *sem; 462 sem_t semtmp; 463 va_list ap; 464 mode_t mode; 465 struct stat sbuf; 466 unsigned int value = 0; 467 468 create = 0; 469 error = 0; 470 fd = -1; 471 sem = SEM_FAILED; 472 473 /* 474 * Bail out if invalid flags specified. 475 */ 476 if (oflag & ~(O_CREAT|O_EXCL)) { 477 errno = EINVAL; 478 return (SEM_FAILED); 479 } 480 481 oflag |= O_RDWR; 482 oflag |= O_CLOEXEC; 483 484 if (name == NULL) { 485 errno = EINVAL; 486 return (SEM_FAILED); 487 } 488 489 _pthread_once(&once, sem_module_init); 490 491 _pthread_mutex_lock(&sem_lock); 492 493 error = get_path(name, path, PATH_MAX, &prefix); 494 if (error) { 495 errno = error; 496 goto error; 497 } 498 499 retry: 500 fd = __sys_open(path, O_RDWR | O_CLOEXEC); 501 502 if (fd > 0) { 503 504 if ((oflag & O_EXCL) == O_EXCL) { 505 __sys_close(fd); 506 errno = EEXIST; 507 goto error; 508 } 509 510 if (_fstat(fd, &sbuf) != 0) { 511 /* Bad things happened, like another thread closing our descriptor */ 512 __sys_close(fd); 513 errno = EINVAL; 514 goto error; 515 } 516 517 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev); 518 519 if (sem != SEM_FAILED) { 520 __sys_close(fd); 521 goto done; 522 } 523 524 if ((sbuf.st_mode & S_IFREG) == 0) { 525 /* We only want regular files here */ 526 __sys_close(fd); 527 errno = EINVAL; 528 goto error; 529 } 530 } else if ((oflag & O_CREAT) && errno == ENOENT) { 531 532 va_start(ap, oflag); 533 534 mode = (mode_t) va_arg(ap, int); 535 value = (unsigned int) va_arg(ap, int); 536 537 va_end(ap); 538 539 if (value > SEM_VALUE_MAX) { 540 errno = EINVAL; 541 goto error; 542 } 543 544 strlcpy(tmppath, prefix, sizeof(tmppath)); 545 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath)); 546 547 if (path_len > sizeof(tmppath)) { 548 errno = ENAMETOOLONG; 549 goto error; 550 } 551 552 553 fd = mkstemp(tmppath); 554 555 if ( fd == -1 ) { 556 errno = EINVAL; 557 goto error; 558 } 559 560 error = fchmod(fd, mode); 561 if ( error == -1 ) { 562 __sys_close(fd); 563 errno = EINVAL; 564 goto error; 565 } 566 567 error = __sys_fcntl(fd, F_SETFD, FD_CLOEXEC); 568 if ( error == -1 ) { 569 __sys_close(fd); 570 errno = EINVAL; 571 goto error; 572 } 573 574 create = 1; 575 } 576 577 if (fd == -1) { 578 switch (errno) { 579 case ENOTDIR: 580 case EISDIR: 581 case EMLINK: 582 case ELOOP: 583 errno = EINVAL; 584 break; 585 case EDQUOT: 586 case EIO: 587 errno = ENOSPC; 588 break; 589 case EROFS: 590 errno = EACCES; 591 } 592 goto error; 593 } 594 595 semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, 596 MAP_NOSYNC | MAP_SHARED, fd, 0); 597 598 if (semtmp == MAP_FAILED) { 599 if (errno != EACCES && errno != EMFILE) 600 errno = ENOMEM; 601 602 if (create) 603 unlink(tmppath); 604 605 __sys_close(fd); 606 goto error; 607 } 608 609 if (create) { 610 ftruncate(fd, sizeof(struct sem)); 611 semtmp->magic = SEM_MAGIC; 612 semtmp->count = (u_int32_t)value; 613 semtmp->semid = SEMID_NAMED; 614 615 if (link(tmppath, path) != 0) { 616 munmap(semtmp, getpagesize()); 617 __sys_close(fd); 618 unlink(tmppath); 619 620 if (errno == EEXIST && (oflag & O_EXCL) == 0) { 621 goto retry; 622 } 623 624 goto error; 625 } 626 unlink(tmppath); 627 628 if (_fstat(fd, &sbuf) != 0) { 629 /* Bad things happened, like another thread closing our descriptor */ 630 munmap(semtmp, getpagesize()); 631 __sys_close(fd); 632 errno = EINVAL; 633 goto error; 634 } 635 636 } 637 sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd); 638 639 done: 640 _pthread_mutex_unlock(&sem_lock); 641 return (sem); 642 643 error: 644 _pthread_mutex_unlock(&sem_lock); 645 return (SEM_FAILED); 646 647 } 648 649 int 650 _sem_close(sem_t *sem) 651 { 652 _pthread_once(&once, sem_module_init); 653 654 _pthread_mutex_lock(&sem_lock); 655 656 if (sem_check_validity(sem)) { 657 _pthread_mutex_unlock(&sem_lock); 658 errno = EINVAL; 659 return (-1); 660 } 661 662 if (sem_close_mapping(sem)) { 663 _pthread_mutex_unlock(&sem_lock); 664 errno = EINVAL; 665 return (-1); 666 } 667 _pthread_mutex_unlock(&sem_lock); 668 669 return (0); 670 } 671 672 int 673 _sem_unlink(const char *name) 674 { 675 char path[PATH_MAX]; 676 const char *prefix; 677 int error; 678 679 error = get_path(name, path, PATH_MAX, &prefix); 680 if (error) { 681 errno = error; 682 return (-1); 683 } 684 685 error = unlink(path); 686 687 if(error) { 688 if (errno != ENAMETOOLONG && errno != ENOENT) 689 errno = EACCES; 690 691 return (-1); 692 } 693 694 return (0); 695 } 696 697 __strong_reference(_sem_destroy, sem_destroy); 698 __strong_reference(_sem_getvalue, sem_getvalue); 699 __strong_reference(_sem_init, sem_init); 700 __strong_reference(_sem_trywait, sem_trywait); 701 __strong_reference(_sem_wait, sem_wait); 702 __strong_reference(_sem_timedwait, sem_timedwait); 703 __strong_reference(_sem_post, sem_post); 704 __strong_reference(_sem_open, sem_open); 705 __strong_reference(_sem_close, sem_close); 706 __strong_reference(_sem_unlink, sem_unlink); 707