xref: /openbsd-src/lib/librthread/rthread_sem_compat.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: rthread_sem_compat.c,v 1.1 2018/06/08 13:53:01 pirofti 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 can_eintr, 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 {
70 		sem->waitcount++;
71 		do {
72 			r = __thrsleep(ident, CLOCK_REALTIME, abstime,
73 			    &sem->lock, delayed_cancel);
74 			_spinlock(&sem->lock);
75 			/* ignore interruptions other than cancelation */
76 			if ((r == ECANCELED && *delayed_cancel == 0) ||
77 			    (r == EINTR && !can_eintr))
78 				r = 0;
79 		} while (r == 0 && sem->value == 0);
80 		sem->waitcount--;
81 		if (r == 0)
82 			sem->value--;
83 	}
84 	_spinunlock(&sem->lock);
85 	return (r);
86 }
87 
88 /* always increment count */
89 int
90 _sem_post(sem_t sem)
91 {
92 	void *ident = (void *)&sem->waitcount;
93 	int rv = 0;
94 
95 	if (sem->shared)
96 		ident = SHARED_IDENT;
97 
98 	_spinlock(&sem->lock);
99 	sem->value++;
100 	if (sem->waitcount) {
101 		__thrwakeup(ident, 1);
102 		rv = 1;
103 	}
104 	_spinunlock(&sem->lock);
105 	return (rv);
106 }
107 
108 /*
109  * exported semaphores
110  */
111 int
112 sem_init(sem_t *semp, int pshared, unsigned int value)
113 {
114 	sem_t sem;
115 
116 	if (value > SEM_VALUE_MAX) {
117 		errno = EINVAL;
118 		return (-1);
119 	}
120 
121 	if (pshared) {
122 		errno = EPERM;
123 		return (-1);
124 #ifdef notyet
125 		char name[SEM_RANDOM_NAME_LEN];
126 		sem_t *sempshared;
127 		int i;
128 
129 		for (;;) {
130 			for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
131 				name[i] = arc4random_uniform(255) + 1;
132 			name[SEM_RANDOM_NAME_LEN - 1] = '\0';
133 			sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value);
134 			if (sempshared != SEM_FAILED)
135 				break;
136 			if (errno == EEXIST)
137 				continue;
138 			if (errno != EPERM)
139 				errno = ENOSPC;
140 			return (-1);
141 		}
142 
143 		/* unnamed semaphore should not be opened twice */
144 		if (sem_unlink(name) == -1) {
145 			sem_close(sempshared);
146 			errno = ENOSPC;
147 			return (-1);
148 		}
149 
150 		*semp = *sempshared;
151 		free(sempshared);
152 		return (0);
153 #endif
154 	}
155 
156 	sem = calloc(1, sizeof(*sem));
157 	if (!sem) {
158 		errno = ENOSPC;
159 		return (-1);
160 	}
161 	sem->lock = _SPINLOCK_UNLOCKED;
162 	sem->value = value;
163 	*semp = sem;
164 
165 	return (0);
166 }
167 
168 int
169 sem_destroy(sem_t *semp)
170 {
171 	sem_t sem;
172 
173 	if (!_threads_ready)		 /* for SEM_MMAP_SIZE */
174 		_rthread_init();
175 
176 	if (!semp || !(sem = *semp)) {
177 		errno = EINVAL;
178 		return (-1);
179 	}
180 
181 	if (sem->waitcount) {
182 #define MSG "sem_destroy on semaphore with waiters!\n"
183 		write(2, MSG, sizeof(MSG) - 1);
184 #undef MSG
185 		errno = EBUSY;
186 		return (-1);
187 	}
188 
189 	*semp = NULL;
190 	if (sem->shared)
191 		munmap(sem, SEM_MMAP_SIZE);
192 	else
193 		free(sem);
194 
195 	return (0);
196 }
197 
198 int
199 sem_getvalue(sem_t *semp, int *sval)
200 {
201 	sem_t sem;
202 
203 	if (!semp || !(sem = *semp)) {
204 		errno = EINVAL;
205 		return (-1);
206 	}
207 
208 	_spinlock(&sem->lock);
209 	*sval = sem->value;
210 	_spinunlock(&sem->lock);
211 
212 	return (0);
213 }
214 
215 int
216 sem_post(sem_t *semp)
217 {
218 	sem_t sem;
219 
220 	if (!semp || !(sem = *semp)) {
221 		errno = EINVAL;
222 		return (-1);
223 	}
224 
225 	_sem_post(sem);
226 
227 	return (0);
228 }
229 
230 int
231 sem_wait(sem_t *semp)
232 {
233 	struct tib *tib = TIB_GET();
234 	pthread_t self;
235 	sem_t sem;
236 	int r;
237 	PREP_CANCEL_POINT(tib);
238 
239 	if (!_threads_ready)
240 		_rthread_init();
241 	self = tib->tib_thread;
242 
243 	if (!semp || !(sem = *semp)) {
244 		errno = EINVAL;
245 		return (-1);
246 	}
247 
248 	ENTER_DELAYED_CANCEL_POINT(tib, self);
249 	r = _sem_wait(sem, 1, NULL, &self->delayed_cancel);
250 	LEAVE_CANCEL_POINT_INNER(tib, r);
251 
252 	if (r) {
253 		errno = r;
254 		return (-1);
255 	}
256 
257 	return (0);
258 }
259 
260 int
261 sem_timedwait(sem_t *semp, const struct timespec *abstime)
262 {
263 	struct tib *tib = TIB_GET();
264 	pthread_t self;
265 	sem_t sem;
266 	int r;
267 	PREP_CANCEL_POINT(tib);
268 
269 	if (!semp || !(sem = *semp) || abstime == NULL ||
270 	    abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
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
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
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 *
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
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
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