1 /* $OpenBSD: rthread_sem.c,v 1.25 2016/09/04 10:13:35 akfaew 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 (!semp || !(sem = *semp)) { 176 errno = EINVAL; 177 return (-1); 178 } 179 180 if (sem->waitcount) { 181 #define MSG "sem_destroy on semaphore with waiters!\n" 182 write(2, MSG, sizeof(MSG) - 1); 183 #undef MSG 184 errno = EBUSY; 185 return (-1); 186 } 187 188 *semp = NULL; 189 if (sem->shared) 190 munmap(sem, SEM_MMAP_SIZE); 191 else 192 free(sem); 193 194 return (0); 195 } 196 197 int 198 sem_getvalue(sem_t *semp, int *sval) 199 { 200 sem_t sem; 201 202 if (!semp || !(sem = *semp)) { 203 errno = EINVAL; 204 return (-1); 205 } 206 207 _spinlock(&sem->lock); 208 *sval = sem->value; 209 _spinunlock(&sem->lock); 210 211 return (0); 212 } 213 214 int 215 sem_post(sem_t *semp) 216 { 217 sem_t sem; 218 219 if (!semp || !(sem = *semp)) { 220 errno = EINVAL; 221 return (-1); 222 } 223 224 _sem_post(sem); 225 226 return (0); 227 } 228 229 int 230 sem_wait(sem_t *semp) 231 { 232 struct tib *tib = TIB_GET(); 233 pthread_t self; 234 sem_t sem; 235 int r; 236 PREP_CANCEL_POINT(tib); 237 238 if (!_threads_ready) 239 _rthread_init(); 240 self = tib->tib_thread; 241 242 if (!semp || !(sem = *semp)) { 243 errno = EINVAL; 244 return (-1); 245 } 246 247 ENTER_DELAYED_CANCEL_POINT(tib, self); 248 r = _sem_wait(sem, 0, NULL, &self->delayed_cancel); 249 LEAVE_CANCEL_POINT_INNER(tib, r); 250 251 if (r) { 252 errno = r; 253 return (-1); 254 } 255 256 return (0); 257 } 258 259 int 260 sem_timedwait(sem_t *semp, const struct timespec *abstime) 261 { 262 struct tib *tib = TIB_GET(); 263 pthread_t self; 264 sem_t sem; 265 int r; 266 PREP_CANCEL_POINT(tib); 267 268 if (!_threads_ready) 269 _rthread_init(); 270 self = tib->tib_thread; 271 272 if (!semp || !(sem = *semp)) { 273 errno = EINVAL; 274 return (-1); 275 } 276 277 ENTER_DELAYED_CANCEL_POINT(tib, self); 278 r = _sem_wait(sem, 0, abstime, &self->delayed_cancel); 279 LEAVE_CANCEL_POINT_INNER(tib, r); 280 281 if (r) { 282 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 283 return (-1); 284 } 285 286 return (0); 287 } 288 289 int 290 sem_trywait(sem_t *semp) 291 { 292 sem_t sem; 293 int r; 294 295 if (!semp || !(sem = *semp)) { 296 errno = EINVAL; 297 return (-1); 298 } 299 300 r = _sem_wait(sem, 1, NULL, NULL); 301 302 if (r) { 303 errno = r; 304 return (-1); 305 } 306 307 return (0); 308 } 309 310 311 static void 312 makesempath(const char *origpath, char *sempath, size_t len) 313 { 314 char buf[SHA256_DIGEST_STRING_LENGTH]; 315 316 SHA256Data(origpath, strlen(origpath), buf); 317 snprintf(sempath, len, "/tmp/%s.sem", buf); 318 } 319 320 sem_t * 321 sem_open(const char *name, int oflag, ...) 322 { 323 char sempath[SEM_PATH_SIZE]; 324 struct stat sb; 325 sem_t sem, *semp; 326 unsigned int value = 0; 327 int created = 0, fd; 328 329 if (!_threads_ready) 330 _rthread_init(); 331 332 if (oflag & ~(O_CREAT | O_EXCL)) { 333 errno = EINVAL; 334 return (SEM_FAILED); 335 } 336 337 if (oflag & O_CREAT) { 338 va_list ap; 339 va_start(ap, oflag); 340 /* 3rd parameter mode is not used */ 341 va_arg(ap, mode_t); 342 value = va_arg(ap, unsigned); 343 va_end(ap); 344 345 if (value > SEM_VALUE_MAX) { 346 errno = EINVAL; 347 return (SEM_FAILED); 348 } 349 } 350 351 makesempath(name, sempath, sizeof(sempath)); 352 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 353 if (fd == -1) 354 return (SEM_FAILED); 355 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 356 close(fd); 357 errno = EINVAL; 358 return (SEM_FAILED); 359 } 360 if (sb.st_uid != geteuid()) { 361 close(fd); 362 errno = EPERM; 363 return (SEM_FAILED); 364 } 365 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 366 if (!(oflag & O_CREAT)) { 367 close(fd); 368 errno = EINVAL; 369 return (SEM_FAILED); 370 } 371 if (sb.st_size != 0) { 372 close(fd); 373 errno = EINVAL; 374 return (SEM_FAILED); 375 } 376 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 377 close(fd); 378 errno = EINVAL; 379 return (SEM_FAILED); 380 } 381 382 created = 1; 383 } 384 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 385 MAP_SHARED, fd, 0); 386 close(fd); 387 if (sem == MAP_FAILED) { 388 errno = EINVAL; 389 return (SEM_FAILED); 390 } 391 semp = malloc(sizeof(*semp)); 392 if (!semp) { 393 munmap(sem, SEM_MMAP_SIZE); 394 errno = ENOSPC; 395 return (SEM_FAILED); 396 } 397 if (created) { 398 sem->lock = _SPINLOCK_UNLOCKED; 399 sem->value = value; 400 sem->shared = 1; 401 } 402 *semp = sem; 403 404 return (semp); 405 } 406 407 int 408 sem_close(sem_t *semp) 409 { 410 sem_t sem; 411 412 if (!semp || !(sem = *semp) || !sem->shared) { 413 errno = EINVAL; 414 return (-1); 415 } 416 417 *semp = NULL; 418 munmap(sem, SEM_MMAP_SIZE); 419 free(semp); 420 421 return (0); 422 } 423 424 int 425 sem_unlink(const char *name) 426 { 427 char sempath[SEM_PATH_SIZE]; 428 429 makesempath(name, sempath, sizeof(sempath)); 430 return (unlink(sempath)); 431 } 432