1 /* $OpenBSD: rthread_sem.c,v 1.33 2022/05/14 14:52:20 cheloha 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 || !timespecisvalid(abstime)) { 258 errno = EINVAL; 259 return (-1); 260 } 261 262 if (!_threads_ready) 263 _rthread_init(); 264 self = tib->tib_thread; 265 266 ENTER_DELAYED_CANCEL_POINT(tib, self); 267 error = _sem_wait(sem, 1, abstime, &self->delayed_cancel); 268 LEAVE_CANCEL_POINT_INNER(tib, error); 269 270 if (error) { 271 errno = (error == EWOULDBLOCK) ? ETIMEDOUT : error; 272 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, 273 sem->value, errno); 274 return (-1); 275 } 276 277 return (0); 278 } 279 280 int 281 sem_trywait(sem_t *semp) 282 { 283 sem_t sem; 284 unsigned int val; 285 286 if (!semp || !(sem = *semp)) { 287 errno = EINVAL; 288 return (-1); 289 } 290 291 while ((val = sem->value) > 0) { 292 if (atomic_cas_uint(&sem->value, val, val - 1) == val) { 293 membar_enter_after_atomic(); 294 return (0); 295 } 296 } 297 298 errno = EAGAIN; 299 _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, sem->value, errno); 300 return (-1); 301 } 302 303 304 static void 305 makesempath(const char *origpath, char *sempath, size_t len) 306 { 307 char buf[SHA256_DIGEST_STRING_LENGTH]; 308 309 SHA256Data(origpath, strlen(origpath), buf); 310 snprintf(sempath, len, "/tmp/%s.sem", buf); 311 } 312 313 sem_t * 314 sem_open(const char *name, int oflag, ...) 315 { 316 char sempath[SEM_PATH_SIZE]; 317 struct stat sb; 318 sem_t sem, *semp; 319 unsigned int value = 0; 320 int created = 0, fd; 321 322 if (!_threads_ready) 323 _rthread_init(); 324 325 if (oflag & ~(O_CREAT | O_EXCL)) { 326 errno = EINVAL; 327 return (SEM_FAILED); 328 } 329 330 if (oflag & O_CREAT) { 331 va_list ap; 332 va_start(ap, oflag); 333 /* 3rd parameter mode is not used */ 334 va_arg(ap, mode_t); 335 value = va_arg(ap, unsigned); 336 va_end(ap); 337 338 if (value > SEM_VALUE_MAX) { 339 errno = EINVAL; 340 return (SEM_FAILED); 341 } 342 } 343 344 makesempath(name, sempath, sizeof(sempath)); 345 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 346 if (fd == -1) 347 return (SEM_FAILED); 348 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 349 close(fd); 350 errno = EINVAL; 351 return (SEM_FAILED); 352 } 353 if (sb.st_uid != geteuid()) { 354 close(fd); 355 errno = EPERM; 356 return (SEM_FAILED); 357 } 358 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 359 if (!(oflag & O_CREAT)) { 360 close(fd); 361 errno = EINVAL; 362 return (SEM_FAILED); 363 } 364 if (sb.st_size != 0) { 365 close(fd); 366 errno = EINVAL; 367 return (SEM_FAILED); 368 } 369 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 370 close(fd); 371 errno = EINVAL; 372 return (SEM_FAILED); 373 } 374 375 created = 1; 376 } 377 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 378 MAP_SHARED, fd, 0); 379 close(fd); 380 if (sem == MAP_FAILED) { 381 errno = EINVAL; 382 return (SEM_FAILED); 383 } 384 semp = malloc(sizeof(*semp)); 385 if (!semp) { 386 munmap(sem, SEM_MMAP_SIZE); 387 errno = ENOSPC; 388 return (SEM_FAILED); 389 } 390 if (created) { 391 sem->value = value; 392 sem->shared = 1; 393 } 394 *semp = sem; 395 396 return (semp); 397 } 398 399 int 400 sem_close(sem_t *semp) 401 { 402 sem_t sem; 403 404 if (!semp || !(sem = *semp) || !sem->shared) { 405 errno = EINVAL; 406 return (-1); 407 } 408 409 *semp = NULL; 410 munmap(sem, SEM_MMAP_SIZE); 411 free(semp); 412 413 return (0); 414 } 415 416 int 417 sem_unlink(const char *name) 418 { 419 char sempath[SEM_PATH_SIZE]; 420 421 makesempath(name, sempath, sizeof(sempath)); 422 return (unlink(sempath)); 423 } 424