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 size_t path_len; 447 int error, fd, create; 448 sem_t *sem; 449 sem_t semtmp; 450 va_list ap; 451 mode_t mode; 452 struct stat sbuf; 453 unsigned int value = 0; 454 455 create = 0; 456 error = 0; 457 fd = -1; 458 sem = SEM_FAILED; 459 460 /* 461 * Bail out if invalid flags specified. 462 */ 463 if (oflag & ~(O_CREAT|O_EXCL)) { 464 errno = EINVAL; 465 return (SEM_FAILED); 466 } 467 468 oflag |= O_RDWR; 469 oflag |= O_CLOEXEC; 470 471 if (name == NULL) { 472 errno = EINVAL; 473 return (SEM_FAILED); 474 } 475 476 _pthread_once(&once, sem_module_init); 477 478 _pthread_mutex_lock(&sem_lock); 479 480 error = get_path(name, path, PATH_MAX, &prefix); 481 if (error) { 482 errno = error; 483 goto error; 484 } 485 486 retry: 487 fd = __sys_open(path, O_RDWR | O_CLOEXEC); 488 489 if (fd > 0) { 490 491 if ((oflag & O_EXCL) == O_EXCL) { 492 __sys_close(fd); 493 errno = EEXIST; 494 goto error; 495 } 496 497 if (_fstat(fd, &sbuf) != 0) { 498 /* Bad things happened, like another thread closing our descriptor */ 499 __sys_close(fd); 500 errno = EINVAL; 501 goto error; 502 } 503 504 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev); 505 506 if (sem != SEM_FAILED) { 507 __sys_close(fd); 508 goto done; 509 } 510 511 if ((sbuf.st_mode & S_IFREG) == 0) { 512 /* We only want regular files here */ 513 __sys_close(fd); 514 errno = EINVAL; 515 goto error; 516 } 517 } else if ((oflag & O_CREAT) && errno == ENOENT) { 518 519 va_start(ap, oflag); 520 521 mode = (mode_t) va_arg(ap, int); 522 value = (unsigned int) va_arg(ap, int); 523 524 va_end(ap); 525 526 if (value > SEM_VALUE_MAX) { 527 errno = EINVAL; 528 goto error; 529 } 530 531 strlcpy(tmppath, prefix, sizeof(tmppath)); 532 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath)); 533 534 if (path_len > sizeof(tmppath)) { 535 errno = ENAMETOOLONG; 536 goto error; 537 } 538 539 540 fd = mkstemp(tmppath); 541 542 if ( fd == -1 ) { 543 errno = EINVAL; 544 goto error; 545 } 546 547 error = fchmod(fd, mode); 548 if ( error == -1 ) { 549 __sys_close(fd); 550 errno = EINVAL; 551 goto error; 552 } 553 554 error = __sys_fcntl(fd, F_SETFD, FD_CLOEXEC); 555 if ( error == -1 ) { 556 __sys_close(fd); 557 errno = EINVAL; 558 goto error; 559 } 560 561 create = 1; 562 } 563 564 if (fd == -1) { 565 switch (errno) { 566 case ENOTDIR: 567 case EISDIR: 568 case EMLINK: 569 case ELOOP: 570 errno = EINVAL; 571 break; 572 case EDQUOT: 573 case EIO: 574 errno = ENOSPC; 575 break; 576 case EROFS: 577 errno = EACCES; 578 } 579 goto error; 580 } 581 582 semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, 583 MAP_NOSYNC | MAP_SHARED, fd, 0); 584 585 if (semtmp == MAP_FAILED) { 586 if (errno != EACCES && errno != EMFILE) 587 errno = ENOMEM; 588 589 if (create) 590 unlink(tmppath); 591 592 __sys_close(fd); 593 goto error; 594 } 595 596 if (create) { 597 ftruncate(fd, sizeof(struct sem)); 598 semtmp->magic = SEM_MAGIC; 599 semtmp->count = (u_int32_t)value; 600 semtmp->semid = SEMID_NAMED; 601 602 if (link(tmppath, path) != 0) { 603 munmap(semtmp, getpagesize()); 604 __sys_close(fd); 605 unlink(tmppath); 606 607 if (errno == EEXIST && (oflag & O_EXCL) == 0) { 608 goto retry; 609 } 610 611 goto error; 612 } 613 unlink(tmppath); 614 615 if (_fstat(fd, &sbuf) != 0) { 616 /* Bad things happened, like another thread closing our descriptor */ 617 munmap(semtmp, getpagesize()); 618 __sys_close(fd); 619 errno = EINVAL; 620 goto error; 621 } 622 623 } 624 sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd); 625 626 done: 627 _pthread_mutex_unlock(&sem_lock); 628 return (sem); 629 630 error: 631 _pthread_mutex_unlock(&sem_lock); 632 return (SEM_FAILED); 633 634 } 635 636 int 637 _sem_close(sem_t *sem) 638 { 639 _pthread_once(&once, sem_module_init); 640 641 _pthread_mutex_lock(&sem_lock); 642 643 if (sem_check_validity(sem)) { 644 _pthread_mutex_unlock(&sem_lock); 645 errno = EINVAL; 646 return (-1); 647 } 648 649 if (sem_close_mapping(sem)) { 650 _pthread_mutex_unlock(&sem_lock); 651 errno = EINVAL; 652 return (-1); 653 } 654 _pthread_mutex_unlock(&sem_lock); 655 656 return (0); 657 } 658 659 int 660 _sem_unlink(const char *name) 661 { 662 char path[PATH_MAX]; 663 const char *prefix; 664 int error; 665 666 error = get_path(name, path, PATH_MAX, &prefix); 667 if (error) { 668 errno = error; 669 return (-1); 670 } 671 672 error = unlink(path); 673 674 if(error) { 675 if (errno != ENAMETOOLONG && errno != ENOENT) 676 errno = EACCES; 677 678 return (-1); 679 } 680 681 return (0); 682 } 683 684 __strong_reference(_sem_destroy, sem_destroy); 685 __strong_reference(_sem_getvalue, sem_getvalue); 686 __strong_reference(_sem_init, sem_init); 687 __strong_reference(_sem_trywait, sem_trywait); 688 __strong_reference(_sem_wait, sem_wait); 689 __strong_reference(_sem_timedwait, sem_timedwait); 690 __strong_reference(_sem_post, sem_post); 691 __strong_reference(_sem_open, sem_open); 692 __strong_reference(_sem_close, sem_close); 693 __strong_reference(_sem_unlink, sem_unlink); 694 695