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