1 /* $OpenBSD: rthread_sem.c,v 1.29 2018/06/08 13:53:01 pirofti 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 #define SHARED_IDENT ((void *)-1) 43 44 /* SHA256_DIGEST_STRING_LENGTH includes nul */ 45 /* "/tmp/" + sha256 + ".sem" */ 46 #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) 47 48 /* long enough to be hard to guess */ 49 #define SEM_RANDOM_NAME_LEN 10 50 51 /* 52 * Size of memory to be mmap()'ed by named semaphores. 53 * Should be >= SEM_PATH_SIZE and page-aligned. 54 */ 55 #define SEM_MMAP_SIZE _thread_pagesize 56 57 /* 58 * Internal implementation of semaphores 59 */ 60 int 61 _sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime, 62 int *delayed_cancel) 63 { 64 int r = 0; 65 int v, ov; 66 67 atomic_inc_int(&sem->waitcount); 68 for (;;) { 69 while ((v = sem->value) > 0) { 70 ov = atomic_cas_uint(&sem->value, v, v - 1); 71 if (ov == v) { 72 membar_enter_after_atomic(); 73 atomic_dec_int(&sem->waitcount); 74 return 0; 75 } 76 } 77 if (r) 78 break; 79 80 r = _twait(&sem->value, 0, CLOCK_REALTIME, abstime); 81 /* ignore interruptions other than cancelation */ 82 if ((r == ECANCELED && *delayed_cancel == 0) || 83 (r == EINTR && !can_eintr) || r == EAGAIN) 84 r = 0; 85 } 86 atomic_dec_int(&sem->waitcount); 87 88 return r; 89 } 90 91 /* always increment count */ 92 int 93 _sem_post(sem_t sem) 94 { 95 membar_exit_before_atomic(); 96 atomic_inc_int(&sem->value); 97 _wake(&sem->value, 1); 98 return 0; 99 } 100 101 /* 102 * exported semaphores 103 */ 104 int 105 sem_init(sem_t *semp, int pshared, unsigned int value) 106 { 107 sem_t sem; 108 109 if (value > SEM_VALUE_MAX) { 110 errno = EINVAL; 111 return (-1); 112 } 113 114 if (pshared) { 115 errno = EPERM; 116 return (-1); 117 #ifdef notyet 118 char name[SEM_RANDOM_NAME_LEN]; 119 sem_t *sempshared; 120 int i; 121 122 for (;;) { 123 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 124 name[i] = arc4random_uniform(255) + 1; 125 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 126 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 127 if (sempshared != SEM_FAILED) 128 break; 129 if (errno == EEXIST) 130 continue; 131 if (errno != EPERM) 132 errno = ENOSPC; 133 return (-1); 134 } 135 136 /* unnamed semaphore should not be opened twice */ 137 if (sem_unlink(name) == -1) { 138 sem_close(sempshared); 139 errno = ENOSPC; 140 return (-1); 141 } 142 143 *semp = *sempshared; 144 free(sempshared); 145 return (0); 146 #endif 147 } 148 149 sem = calloc(1, sizeof(*sem)); 150 if (!sem) { 151 errno = ENOSPC; 152 return (-1); 153 } 154 sem->value = value; 155 *semp = sem; 156 157 return (0); 158 } 159 160 int 161 sem_destroy(sem_t *semp) 162 { 163 sem_t sem; 164 165 if (!_threads_ready) /* for SEM_MMAP_SIZE */ 166 _rthread_init(); 167 168 if (!semp || !(sem = *semp)) { 169 errno = EINVAL; 170 return (-1); 171 } 172 173 if (sem->waitcount) { 174 #define MSG "sem_destroy on semaphore with waiters!\n" 175 write(2, MSG, sizeof(MSG) - 1); 176 #undef MSG 177 errno = EBUSY; 178 return (-1); 179 } 180 181 *semp = NULL; 182 if (sem->shared) 183 munmap(sem, SEM_MMAP_SIZE); 184 else 185 free(sem); 186 187 return (0); 188 } 189 190 int 191 sem_getvalue(sem_t *semp, int *sval) 192 { 193 sem_t sem; 194 195 if (!semp || !(sem = *semp)) { 196 errno = EINVAL; 197 return (-1); 198 } 199 200 *sval = sem->value; 201 202 return (0); 203 } 204 205 int 206 sem_post(sem_t *semp) 207 { 208 sem_t sem; 209 210 if (!semp || !(sem = *semp)) { 211 errno = EINVAL; 212 return (-1); 213 } 214 215 _sem_post(sem); 216 217 return (0); 218 } 219 220 int 221 sem_wait(sem_t *semp) 222 { 223 struct tib *tib = TIB_GET(); 224 pthread_t self; 225 sem_t sem; 226 int r; 227 PREP_CANCEL_POINT(tib); 228 229 if (!_threads_ready) 230 _rthread_init(); 231 self = tib->tib_thread; 232 233 if (!semp || !(sem = *semp)) { 234 errno = EINVAL; 235 return (-1); 236 } 237 238 ENTER_DELAYED_CANCEL_POINT(tib, self); 239 r = _sem_wait(sem, 1, NULL, &self->delayed_cancel); 240 LEAVE_CANCEL_POINT_INNER(tib, r); 241 242 if (r) { 243 errno = r; 244 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, 245 sem->value, errno); 246 return (-1); 247 } 248 249 return (0); 250 } 251 252 int 253 sem_timedwait(sem_t *semp, const struct timespec *abstime) 254 { 255 struct tib *tib = TIB_GET(); 256 pthread_t self; 257 sem_t sem; 258 int r; 259 PREP_CANCEL_POINT(tib); 260 261 if (!semp || !(sem = *semp) || abstime == NULL || 262 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { 263 errno = EINVAL; 264 return (-1); 265 } 266 267 if (!_threads_ready) 268 _rthread_init(); 269 self = tib->tib_thread; 270 271 ENTER_DELAYED_CANCEL_POINT(tib, self); 272 r = _sem_wait(sem, 1, abstime, &self->delayed_cancel); 273 LEAVE_CANCEL_POINT_INNER(tib, r); 274 275 if (r) { 276 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 277 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, 278 sem->value, errno); 279 return (-1); 280 } 281 282 return (0); 283 } 284 285 int 286 sem_trywait(sem_t *semp) 287 { 288 sem_t sem; 289 int v, ov; 290 291 if (!semp || !(sem = *semp)) { 292 errno = EINVAL; 293 return (-1); 294 } 295 296 while ((v = sem->value) > 0) { 297 ov = atomic_cas_uint(&sem->value, v, v - 1); 298 if (ov == v) { 299 membar_enter_after_atomic(); 300 return (0); 301 } 302 } 303 304 errno = EAGAIN; 305 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, sem->value, errno); 306 return (-1); 307 } 308 309 310 static void 311 makesempath(const char *origpath, char *sempath, size_t len) 312 { 313 char buf[SHA256_DIGEST_STRING_LENGTH]; 314 315 SHA256Data(origpath, strlen(origpath), buf); 316 snprintf(sempath, len, "/tmp/%s.sem", buf); 317 } 318 319 sem_t * 320 sem_open(const char *name, int oflag, ...) 321 { 322 char sempath[SEM_PATH_SIZE]; 323 struct stat sb; 324 sem_t sem, *semp; 325 unsigned int value = 0; 326 int created = 0, fd; 327 328 if (!_threads_ready) 329 _rthread_init(); 330 331 if (oflag & ~(O_CREAT | O_EXCL)) { 332 errno = EINVAL; 333 return (SEM_FAILED); 334 } 335 336 if (oflag & O_CREAT) { 337 va_list ap; 338 va_start(ap, oflag); 339 /* 3rd parameter mode is not used */ 340 va_arg(ap, mode_t); 341 value = va_arg(ap, unsigned); 342 va_end(ap); 343 344 if (value > SEM_VALUE_MAX) { 345 errno = EINVAL; 346 return (SEM_FAILED); 347 } 348 } 349 350 makesempath(name, sempath, sizeof(sempath)); 351 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 352 if (fd == -1) 353 return (SEM_FAILED); 354 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 355 close(fd); 356 errno = EINVAL; 357 return (SEM_FAILED); 358 } 359 if (sb.st_uid != geteuid()) { 360 close(fd); 361 errno = EPERM; 362 return (SEM_FAILED); 363 } 364 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 365 if (!(oflag & O_CREAT)) { 366 close(fd); 367 errno = EINVAL; 368 return (SEM_FAILED); 369 } 370 if (sb.st_size != 0) { 371 close(fd); 372 errno = EINVAL; 373 return (SEM_FAILED); 374 } 375 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 376 close(fd); 377 errno = EINVAL; 378 return (SEM_FAILED); 379 } 380 381 created = 1; 382 } 383 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 384 MAP_SHARED, fd, 0); 385 close(fd); 386 if (sem == MAP_FAILED) { 387 errno = EINVAL; 388 return (SEM_FAILED); 389 } 390 semp = malloc(sizeof(*semp)); 391 if (!semp) { 392 munmap(sem, SEM_MMAP_SIZE); 393 errno = ENOSPC; 394 return (SEM_FAILED); 395 } 396 if (created) { 397 sem->value = value; 398 sem->shared = 1; 399 } 400 *semp = sem; 401 402 return (semp); 403 } 404 405 int 406 sem_close(sem_t *semp) 407 { 408 sem_t sem; 409 410 if (!semp || !(sem = *semp) || !sem->shared) { 411 errno = EINVAL; 412 return (-1); 413 } 414 415 *semp = NULL; 416 munmap(sem, SEM_MMAP_SIZE); 417 free(semp); 418 419 return (0); 420 } 421 422 int 423 sem_unlink(const char *name) 424 { 425 char sempath[SEM_PATH_SIZE]; 426 427 makesempath(name, sempath, sizeof(sempath)); 428 return (unlink(sempath)); 429 } 430