1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice(s), this list of conditions and the following disclaimer as 12 * the first lines of this file unmodified other than the possible 13 * addition of one or more copyright notices. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice(s), this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 23 * 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 26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 28 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "namespace.h" 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <errno.h> 38 #include <machine/atomic.h> 39 #include <sys/umtx.h> 40 #include <limits.h> 41 #include <fcntl.h> 42 #include <pthread.h> 43 #include <stdarg.h> 44 #include <stdbool.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <semaphore.h> 49 #include <unistd.h> 50 #include "un-namespace.h" 51 #include "libc_private.h" 52 53 __weak_reference(_sem_close, sem_close); 54 __weak_reference(_sem_destroy, sem_destroy); 55 __weak_reference(_sem_getvalue, sem_getvalue); 56 __weak_reference(_sem_init, sem_init); 57 __weak_reference(_sem_open, sem_open); 58 __weak_reference(_sem_post, sem_post); 59 __weak_reference(_sem_timedwait, sem_timedwait); 60 __weak_reference(_sem_clockwait_np, sem_clockwait_np); 61 __weak_reference(_sem_trywait, sem_trywait); 62 __weak_reference(_sem_unlink, sem_unlink); 63 __weak_reference(_sem_wait, sem_wait); 64 65 #define SEM_PREFIX "/tmp/SEMD" 66 #define SEM_MAGIC ((u_int32_t)0x73656d32) 67 68 _Static_assert(SEM_VALUE_MAX <= USEM_MAX_COUNT, "SEM_VALUE_MAX too large"); 69 70 struct sem_nameinfo { 71 int open_count; 72 char *name; 73 dev_t dev; 74 ino_t ino; 75 sem_t *sem; 76 LIST_ENTRY(sem_nameinfo) next; 77 }; 78 79 static pthread_once_t once = PTHREAD_ONCE_INIT; 80 static pthread_mutex_t sem_llock; 81 static LIST_HEAD(, sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 82 83 static void 84 sem_prefork(void) 85 { 86 87 _pthread_mutex_lock(&sem_llock); 88 } 89 90 static void 91 sem_postfork(void) 92 { 93 94 _pthread_mutex_unlock(&sem_llock); 95 } 96 97 static void 98 sem_child_postfork(void) 99 { 100 101 _pthread_mutex_unlock(&sem_llock); 102 } 103 104 static void 105 sem_module_init(void) 106 { 107 108 _pthread_mutex_init(&sem_llock, NULL); 109 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 110 } 111 112 static inline int 113 sem_check_validity(sem_t *sem) 114 { 115 116 if (sem->_magic == SEM_MAGIC) 117 return (0); 118 errno = EINVAL; 119 return (-1); 120 } 121 122 int 123 _sem_init(sem_t *sem, int pshared, unsigned int value) 124 { 125 126 if (value > SEM_VALUE_MAX) { 127 errno = EINVAL; 128 return (-1); 129 } 130 131 bzero(sem, sizeof(sem_t)); 132 sem->_magic = SEM_MAGIC; 133 sem->_kern._count = (u_int32_t)value; 134 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 135 return (0); 136 } 137 138 sem_t * 139 _sem_open(const char *name, int flags, ...) 140 { 141 char path[PATH_MAX]; 142 struct stat sb; 143 va_list ap; 144 struct sem_nameinfo *ni; 145 sem_t *sem, tmp; 146 int errsave, fd, len, mode, value; 147 148 ni = NULL; 149 sem = NULL; 150 fd = -1; 151 value = 0; 152 153 if (name[0] != '/') { 154 errno = EINVAL; 155 return (SEM_FAILED); 156 } 157 name++; 158 strcpy(path, SEM_PREFIX); 159 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 160 errno = ENAMETOOLONG; 161 return (SEM_FAILED); 162 } 163 if (flags & ~(O_CREAT|O_EXCL)) { 164 errno = EINVAL; 165 return (SEM_FAILED); 166 } 167 if ((flags & O_CREAT) != 0) { 168 va_start(ap, flags); 169 mode = va_arg(ap, int); 170 value = va_arg(ap, int); 171 va_end(ap); 172 } 173 fd = -1; 174 _pthread_once(&once, sem_module_init); 175 176 _pthread_mutex_lock(&sem_llock); 177 LIST_FOREACH(ni, &sem_list, next) { 178 if (ni->name != NULL && strcmp(name, ni->name) == 0) { 179 fd = _open(path, flags | O_RDWR | O_CLOEXEC | 180 O_EXLOCK, mode); 181 if (fd == -1 || _fstat(fd, &sb) == -1) { 182 ni = NULL; 183 goto error; 184 } 185 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 186 O_EXCL) || ni->dev != sb.st_dev || 187 ni->ino != sb.st_ino) { 188 ni->name = NULL; 189 ni = NULL; 190 break; 191 } 192 ni->open_count++; 193 sem = ni->sem; 194 _pthread_mutex_unlock(&sem_llock); 195 _close(fd); 196 return (sem); 197 } 198 } 199 200 len = sizeof(*ni) + strlen(name) + 1; 201 ni = (struct sem_nameinfo *)malloc(len); 202 if (ni == NULL) { 203 errno = ENOSPC; 204 goto error; 205 } 206 207 ni->name = (char *)(ni+1); 208 strcpy(ni->name, name); 209 210 if (fd == -1) { 211 fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 212 if (fd == -1 || _fstat(fd, &sb) == -1) 213 goto error; 214 } 215 if (sb.st_size < sizeof(sem_t)) { 216 tmp._magic = SEM_MAGIC; 217 tmp._kern._count = value; 218 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 219 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 220 goto error; 221 } 222 flock(fd, LOCK_UN); 223 sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, 224 MAP_SHARED | MAP_NOSYNC, fd, 0); 225 if (sem == MAP_FAILED) { 226 sem = NULL; 227 if (errno == ENOMEM) 228 errno = ENOSPC; 229 goto error; 230 } 231 if (sem->_magic != SEM_MAGIC) { 232 errno = EINVAL; 233 goto error; 234 } 235 ni->open_count = 1; 236 ni->sem = sem; 237 ni->dev = sb.st_dev; 238 ni->ino = sb.st_ino; 239 LIST_INSERT_HEAD(&sem_list, ni, next); 240 _close(fd); 241 _pthread_mutex_unlock(&sem_llock); 242 return (sem); 243 244 error: 245 errsave = errno; 246 if (fd != -1) 247 _close(fd); 248 if (sem != NULL) 249 munmap(sem, sizeof(sem_t)); 250 free(ni); 251 _pthread_mutex_unlock(&sem_llock); 252 errno = errsave; 253 return (SEM_FAILED); 254 } 255 256 int 257 _sem_close(sem_t *sem) 258 { 259 struct sem_nameinfo *ni; 260 bool last; 261 262 if (sem_check_validity(sem) != 0) 263 return (-1); 264 265 if (!(sem->_kern._flags & SEM_NAMED)) { 266 errno = EINVAL; 267 return (-1); 268 } 269 270 _pthread_once(&once, sem_module_init); 271 272 _pthread_mutex_lock(&sem_llock); 273 LIST_FOREACH(ni, &sem_list, next) { 274 if (sem == ni->sem) { 275 last = --ni->open_count == 0; 276 if (last) 277 LIST_REMOVE(ni, next); 278 _pthread_mutex_unlock(&sem_llock); 279 if (last) { 280 munmap(sem, sizeof(*sem)); 281 free(ni); 282 } 283 return (0); 284 } 285 } 286 _pthread_mutex_unlock(&sem_llock); 287 errno = EINVAL; 288 return (-1); 289 } 290 291 int 292 _sem_unlink(const char *name) 293 { 294 char path[PATH_MAX]; 295 296 if (name[0] != '/') { 297 errno = ENOENT; 298 return -1; 299 } 300 name++; 301 strcpy(path, SEM_PREFIX); 302 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 303 errno = ENAMETOOLONG; 304 return (-1); 305 } 306 307 return (unlink(path)); 308 } 309 310 int 311 _sem_destroy(sem_t *sem) 312 { 313 314 if (sem_check_validity(sem) != 0) 315 return (-1); 316 317 if (sem->_kern._flags & SEM_NAMED) { 318 errno = EINVAL; 319 return (-1); 320 } 321 sem->_magic = 0; 322 return (0); 323 } 324 325 int 326 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 327 { 328 329 if (sem_check_validity(sem) != 0) 330 return (-1); 331 332 *sval = (int)USEM_COUNT(sem->_kern._count); 333 return (0); 334 } 335 336 static __inline int 337 usem_wake(struct _usem2 *sem) 338 { 339 340 return (_umtx_op(sem, UMTX_OP_SEM2_WAKE, 0, NULL, NULL)); 341 } 342 343 static __inline int 344 usem_wait(struct _usem2 *sem, clockid_t clock_id, int flags, 345 const struct timespec *rqtp, struct timespec *rmtp) 346 { 347 struct { 348 struct _umtx_time timeout; 349 struct timespec remain; 350 } tms; 351 void *tm_p; 352 size_t tm_size; 353 int retval; 354 355 if (rqtp == NULL) { 356 tm_p = NULL; 357 tm_size = 0; 358 } else { 359 tms.timeout._clockid = clock_id; 360 tms.timeout._flags = (flags & TIMER_ABSTIME) ? UMTX_ABSTIME : 0; 361 tms.timeout._timeout = *rqtp; 362 tm_p = &tms; 363 tm_size = sizeof(tms); 364 } 365 retval = _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, (void *)tm_size, tm_p); 366 if (retval == -1 && errno == EINTR && (flags & TIMER_ABSTIME) == 0 && 367 rqtp != NULL && rmtp != NULL) { 368 *rmtp = tms.remain; 369 } 370 371 return (retval); 372 } 373 374 int 375 _sem_trywait(sem_t *sem) 376 { 377 int val; 378 379 if (sem_check_validity(sem) != 0) 380 return (-1); 381 382 while (USEM_COUNT(val = sem->_kern._count) > 0) { 383 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 384 return (0); 385 } 386 errno = EAGAIN; 387 return (-1); 388 } 389 390 int 391 _sem_clockwait_np(sem_t * __restrict sem, clockid_t clock_id, int flags, 392 const struct timespec *rqtp, struct timespec *rmtp) 393 { 394 int val, retval; 395 396 if (sem_check_validity(sem) != 0) 397 return (-1); 398 399 retval = 0; 400 _pthread_testcancel(); 401 for (;;) { 402 while (USEM_COUNT(val = sem->_kern._count) > 0) { 403 if (atomic_cmpset_acq_int(&sem->_kern._count, val, 404 val - 1)) 405 return (0); 406 } 407 408 if (retval) { 409 _pthread_testcancel(); 410 break; 411 } 412 413 /* 414 * The timeout argument is only supposed to 415 * be checked if the thread would have blocked. 416 */ 417 if (rqtp != NULL) { 418 if (rqtp->tv_nsec >= 1000000000 || rqtp->tv_nsec < 0) { 419 errno = EINVAL; 420 return (-1); 421 } 422 } 423 _pthread_cancel_enter(1); 424 retval = usem_wait(&sem->_kern, clock_id, flags, rqtp, rmtp); 425 _pthread_cancel_leave(0); 426 } 427 return (retval); 428 } 429 430 int 431 _sem_timedwait(sem_t * __restrict sem, 432 const struct timespec * __restrict abstime) 433 { 434 435 return (_sem_clockwait_np(sem, CLOCK_REALTIME, TIMER_ABSTIME, abstime, 436 NULL)); 437 }; 438 439 int 440 _sem_wait(sem_t *sem) 441 { 442 443 return (_sem_timedwait(sem, NULL)); 444 } 445 446 /* 447 * POSIX: 448 * The sem_post() interface is reentrant with respect to signals and may be 449 * invoked from a signal-catching function. 450 * The implementation does not use lock, so it should be safe. 451 */ 452 int 453 _sem_post(sem_t *sem) 454 { 455 unsigned int count; 456 457 if (sem_check_validity(sem) != 0) 458 return (-1); 459 460 do { 461 count = sem->_kern._count; 462 if (USEM_COUNT(count) + 1 > SEM_VALUE_MAX) { 463 errno = EOVERFLOW; 464 return (-1); 465 } 466 } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, count + 1)); 467 if (count & USEM_HAS_WAITERS) 468 usem_wake(&sem->_kern); 469 return (0); 470 } 471