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