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