1 /* $OpenBSD: rthread_sem.c,v 1.30 2019/01/29 17:43:23 mpi Exp $ */ 2 /* 3 * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org> 4 * Copyright (c) 2018 Paul Irofti <pirofti@openbsd.org> 5 * All Rights Reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/mman.h> 22 #include <sys/stat.h> 23 #include <sys/atomic.h> 24 #include <sys/time.h> 25 #include <sys/futex.h> 26 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <sha2.h> 30 #include <stdarg.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include <pthread.h> 37 38 #include "rthread.h" 39 #include "cancel.h" /* in libc/include */ 40 #include "synch.h" 41 42 /* SHA256_DIGEST_STRING_LENGTH includes nul */ 43 /* "/tmp/" + sha256 + ".sem" */ 44 #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) 45 46 /* long enough to be hard to guess */ 47 #define SEM_RANDOM_NAME_LEN 10 48 49 /* 50 * Size of memory to be mmap()'ed by named semaphores. 51 * Should be >= SEM_PATH_SIZE and page-aligned. 52 */ 53 #define SEM_MMAP_SIZE _thread_pagesize 54 55 /* 56 * Internal implementation of semaphores 57 */ 58 int 59 _sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime, 60 int *delayed_cancel) 61 { 62 unsigned int val; 63 int error = 0; 64 65 atomic_inc_int(&sem->waitcount); 66 for (;;) { 67 while ((val = sem->value) > 0) { 68 if (atomic_cas_uint(&sem->value, val, val - 1) == val) { 69 membar_enter_after_atomic(); 70 atomic_dec_int(&sem->waitcount); 71 return (0); 72 } 73 } 74 if (error) 75 break; 76 77 error = _twait(&sem->value, 0, CLOCK_REALTIME, abstime); 78 /* ignore interruptions other than cancelation */ 79 if ((error == ECANCELED && *delayed_cancel == 0) || 80 (error == EINTR && !can_eintr) || error == EAGAIN) 81 error = 0; 82 } 83 atomic_dec_int(&sem->waitcount); 84 85 return (error); 86 } 87 88 /* always increment count */ 89 int 90 _sem_post(sem_t sem) 91 { 92 membar_exit_before_atomic(); 93 atomic_inc_int(&sem->value); 94 _wake(&sem->value, 1); 95 return 0; 96 } 97 98 /* 99 * exported semaphores 100 */ 101 int 102 sem_init(sem_t *semp, int pshared, unsigned int value) 103 { 104 sem_t sem; 105 106 if (value > SEM_VALUE_MAX) { 107 errno = EINVAL; 108 return (-1); 109 } 110 111 if (pshared) { 112 errno = EPERM; 113 return (-1); 114 #ifdef notyet 115 char name[SEM_RANDOM_NAME_LEN]; 116 sem_t *sempshared; 117 int i; 118 119 for (;;) { 120 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 121 name[i] = arc4random_uniform(255) + 1; 122 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 123 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 124 if (sempshared != SEM_FAILED) 125 break; 126 if (errno == EEXIST) 127 continue; 128 if (errno != EPERM) 129 errno = ENOSPC; 130 return (-1); 131 } 132 133 /* unnamed semaphore should not be opened twice */ 134 if (sem_unlink(name) == -1) { 135 sem_close(sempshared); 136 errno = ENOSPC; 137 return (-1); 138 } 139 140 *semp = *sempshared; 141 free(sempshared); 142 return (0); 143 #endif 144 } 145 146 sem = calloc(1, sizeof(*sem)); 147 if (!sem) { 148 errno = ENOSPC; 149 return (-1); 150 } 151 sem->value = value; 152 *semp = sem; 153 154 return (0); 155 } 156 157 int 158 sem_destroy(sem_t *semp) 159 { 160 sem_t sem; 161 162 if (!_threads_ready) /* for SEM_MMAP_SIZE */ 163 _rthread_init(); 164 165 if (!semp || !(sem = *semp)) { 166 errno = EINVAL; 167 return (-1); 168 } 169 170 if (sem->waitcount) { 171 #define MSG "sem_destroy on semaphore with waiters!\n" 172 write(2, MSG, sizeof(MSG) - 1); 173 #undef MSG 174 errno = EBUSY; 175 return (-1); 176 } 177 178 *semp = NULL; 179 if (sem->shared) 180 munmap(sem, SEM_MMAP_SIZE); 181 else 182 free(sem); 183 184 return (0); 185 } 186 187 int 188 sem_getvalue(sem_t *semp, int *sval) 189 { 190 sem_t sem; 191 192 if (!semp || !(sem = *semp)) { 193 errno = EINVAL; 194 return (-1); 195 } 196 197 *sval = sem->value; 198 199 return (0); 200 } 201 202 int 203 sem_post(sem_t *semp) 204 { 205 sem_t sem; 206 207 if (!semp || !(sem = *semp)) { 208 errno = EINVAL; 209 return (-1); 210 } 211 212 _sem_post(sem); 213 214 return (0); 215 } 216 217 int 218 sem_wait(sem_t *semp) 219 { 220 struct tib *tib = TIB_GET(); 221 pthread_t self; 222 sem_t sem; 223 int error; 224 PREP_CANCEL_POINT(tib); 225 226 if (!_threads_ready) 227 _rthread_init(); 228 self = tib->tib_thread; 229 230 if (!semp || !(sem = *semp)) { 231 errno = EINVAL; 232 return (-1); 233 } 234 235 ENTER_DELAYED_CANCEL_POINT(tib, self); 236 error = _sem_wait(sem, 1, NULL, &self->delayed_cancel); 237 LEAVE_CANCEL_POINT_INNER(tib, error); 238 239 if (error) { 240 errno = error; 241 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, 242 sem->value, errno); 243 return (-1); 244 } 245 246 return (0); 247 } 248 249 int 250 sem_timedwait(sem_t *semp, const struct timespec *abstime) 251 { 252 struct tib *tib = TIB_GET(); 253 pthread_t self; 254 sem_t sem; 255 int error; 256 PREP_CANCEL_POINT(tib); 257 258 if (!semp || !(sem = *semp) || abstime == NULL || 259 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { 260 errno = EINVAL; 261 return (-1); 262 } 263 264 if (!_threads_ready) 265 _rthread_init(); 266 self = tib->tib_thread; 267 268 ENTER_DELAYED_CANCEL_POINT(tib, self); 269 error = _sem_wait(sem, 1, abstime, &self->delayed_cancel); 270 LEAVE_CANCEL_POINT_INNER(tib, error); 271 272 if (error) { 273 errno = (error == EWOULDBLOCK) ? ETIMEDOUT : error; 274 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, 275 sem->value, errno); 276 return (-1); 277 } 278 279 return (0); 280 } 281 282 int 283 sem_trywait(sem_t *semp) 284 { 285 sem_t sem; 286 unsigned int val; 287 288 if (!semp || !(sem = *semp)) { 289 errno = EINVAL; 290 return (-1); 291 } 292 293 while ((val = sem->value) > 0) { 294 if (atomic_cas_uint(&sem->value, val, val - 1) == val) { 295 membar_enter_after_atomic(); 296 return (0); 297 } 298 } 299 300 errno = EAGAIN; 301 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, sem->value, errno); 302 return (-1); 303 } 304 305 306 static void 307 makesempath(const char *origpath, char *sempath, size_t len) 308 { 309 char buf[SHA256_DIGEST_STRING_LENGTH]; 310 311 SHA256Data(origpath, strlen(origpath), buf); 312 snprintf(sempath, len, "/tmp/%s.sem", buf); 313 } 314 315 sem_t * 316 sem_open(const char *name, int oflag, ...) 317 { 318 char sempath[SEM_PATH_SIZE]; 319 struct stat sb; 320 sem_t sem, *semp; 321 unsigned int value = 0; 322 int created = 0, fd; 323 324 if (!_threads_ready) 325 _rthread_init(); 326 327 if (oflag & ~(O_CREAT | O_EXCL)) { 328 errno = EINVAL; 329 return (SEM_FAILED); 330 } 331 332 if (oflag & O_CREAT) { 333 va_list ap; 334 va_start(ap, oflag); 335 /* 3rd parameter mode is not used */ 336 va_arg(ap, mode_t); 337 value = va_arg(ap, unsigned); 338 va_end(ap); 339 340 if (value > SEM_VALUE_MAX) { 341 errno = EINVAL; 342 return (SEM_FAILED); 343 } 344 } 345 346 makesempath(name, sempath, sizeof(sempath)); 347 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 348 if (fd == -1) 349 return (SEM_FAILED); 350 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 351 close(fd); 352 errno = EINVAL; 353 return (SEM_FAILED); 354 } 355 if (sb.st_uid != geteuid()) { 356 close(fd); 357 errno = EPERM; 358 return (SEM_FAILED); 359 } 360 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 361 if (!(oflag & O_CREAT)) { 362 close(fd); 363 errno = EINVAL; 364 return (SEM_FAILED); 365 } 366 if (sb.st_size != 0) { 367 close(fd); 368 errno = EINVAL; 369 return (SEM_FAILED); 370 } 371 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 372 close(fd); 373 errno = EINVAL; 374 return (SEM_FAILED); 375 } 376 377 created = 1; 378 } 379 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 380 MAP_SHARED, fd, 0); 381 close(fd); 382 if (sem == MAP_FAILED) { 383 errno = EINVAL; 384 return (SEM_FAILED); 385 } 386 semp = malloc(sizeof(*semp)); 387 if (!semp) { 388 munmap(sem, SEM_MMAP_SIZE); 389 errno = ENOSPC; 390 return (SEM_FAILED); 391 } 392 if (created) { 393 sem->value = value; 394 sem->shared = 1; 395 } 396 *semp = sem; 397 398 return (semp); 399 } 400 401 int 402 sem_close(sem_t *semp) 403 { 404 sem_t sem; 405 406 if (!semp || !(sem = *semp) || !sem->shared) { 407 errno = EINVAL; 408 return (-1); 409 } 410 411 *semp = NULL; 412 munmap(sem, SEM_MMAP_SIZE); 413 free(semp); 414 415 return (0); 416 } 417 418 int 419 sem_unlink(const char *name) 420 { 421 char sempath[SEM_PATH_SIZE]; 422 423 makesempath(name, sempath, sizeof(sempath)); 424 return (unlink(sempath)); 425 } 426