xref: /openbsd-src/lib/librthread/rthread_sem.c (revision c554a768c1617ab8d979dfdbb7ffdf8827e11e8e)
1*c554a768Scheloha /*	$OpenBSD: rthread_sem.c,v 1.33 2022/05/14 14:52:20 cheloha Exp $ */
24afbc249Smpi /*
34a5ade84Stedu  * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org>
48855e28cSpirofti  * Copyright (c) 2018 Paul Irofti <paul@irofti.net>
54afbc249Smpi  * All Rights Reserved.
64afbc249Smpi  *
74afbc249Smpi  * Permission to use, copy, modify, and distribute this software for any
84afbc249Smpi  * purpose with or without fee is hereby granted, provided that the above
94afbc249Smpi  * copyright notice and this permission notice appear in all copies.
104afbc249Smpi  *
114afbc249Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
124afbc249Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
134afbc249Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
144afbc249Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
154afbc249Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
164afbc249Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
174afbc249Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
184afbc249Smpi  */
19aea60beeSderaadt 
204a5ade84Stedu #include <sys/types.h>
214a5ade84Stedu #include <sys/mman.h>
224a5ade84Stedu #include <sys/stat.h>
234b11781bSpirofti #include <sys/atomic.h>
244b11781bSpirofti #include <sys/time.h>
254afbc249Smpi 
264afbc249Smpi #include <errno.h>
274a5ade84Stedu #include <fcntl.h>
284a5ade84Stedu #include <sha2.h>
29a5de71bfStedu #include <stdarg.h>
304a5ade84Stedu #include <stdlib.h>
314a5ade84Stedu #include <stdio.h>
324a5ade84Stedu #include <string.h>
334a5ade84Stedu #include <unistd.h>
344afbc249Smpi 
354afbc249Smpi #include <pthread.h>
364afbc249Smpi 
374afbc249Smpi #include "rthread.h"
38fe38b55cSguenther #include "cancel.h"		/* in libc/include */
394b11781bSpirofti #include "synch.h"
404afbc249Smpi 
414a5ade84Stedu /* SHA256_DIGEST_STRING_LENGTH includes nul */
424a5ade84Stedu /* "/tmp/" + sha256 + ".sem" */
434a5ade84Stedu #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4)
444a5ade84Stedu 
454a5ade84Stedu /* long enough to be hard to guess */
46e79fb398Sfgsch #define SEM_RANDOM_NAME_LEN	10
474a5ade84Stedu 
484a5ade84Stedu /*
494a5ade84Stedu  * Size of memory to be mmap()'ed by named semaphores.
504a5ade84Stedu  * Should be >= SEM_PATH_SIZE and page-aligned.
514a5ade84Stedu  */
527567a0bfSguenther #define SEM_MMAP_SIZE	_thread_pagesize
534a5ade84Stedu 
544afbc249Smpi /*
554afbc249Smpi  * Internal implementation of semaphores
564afbc249Smpi  */
574afbc249Smpi int
_sem_wait(sem_t sem,int can_eintr,const struct timespec * abstime,int * delayed_cancel)58620a9b40Sguenther _sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime,
5937ebc96dSguenther     int *delayed_cancel)
604afbc249Smpi {
61ada67568Smpi 	unsigned int val;
62ada67568Smpi 	int error = 0;
634afbc249Smpi 
644b11781bSpirofti 	atomic_inc_int(&sem->waitcount);
654b11781bSpirofti 	for (;;) {
66ada67568Smpi 		while ((val = sem->value) > 0) {
67ada67568Smpi 			if (atomic_cas_uint(&sem->value, val, val - 1) == val) {
684b11781bSpirofti 				membar_enter_after_atomic();
694b11781bSpirofti 				atomic_dec_int(&sem->waitcount);
70ada67568Smpi 				return (0);
714b11781bSpirofti 			}
724b11781bSpirofti 		}
73ada67568Smpi 		if (error)
744b11781bSpirofti 			break;
754a5ade84Stedu 
76ada67568Smpi 		error = _twait(&sem->value, 0, CLOCK_REALTIME, abstime);
7737ebc96dSguenther 		/* ignore interruptions other than cancelation */
78ada67568Smpi 		if ((error == ECANCELED && *delayed_cancel == 0) ||
79ada67568Smpi 		    (error == EINTR && !can_eintr) || error == EAGAIN)
80ada67568Smpi 			error = 0;
814afbc249Smpi 	}
824b11781bSpirofti 	atomic_dec_int(&sem->waitcount);
834b11781bSpirofti 
84ada67568Smpi 	return (error);
854afbc249Smpi }
864afbc249Smpi 
874afbc249Smpi /* always increment count */
884afbc249Smpi int
_sem_post(sem_t sem)894afbc249Smpi _sem_post(sem_t sem)
904afbc249Smpi {
914b11781bSpirofti 	membar_exit_before_atomic();
924b11781bSpirofti 	atomic_inc_int(&sem->value);
934b11781bSpirofti 	_wake(&sem->value, 1);
944b11781bSpirofti 	return 0;
954afbc249Smpi }
964afbc249Smpi 
974afbc249Smpi /*
984afbc249Smpi  * exported semaphores
994afbc249Smpi  */
1004afbc249Smpi int
sem_init(sem_t * semp,int pshared,unsigned int value)1014afbc249Smpi sem_init(sem_t *semp, int pshared, unsigned int value)
1024afbc249Smpi {
103378aca9aStedu 	sem_t sem;
1044afbc249Smpi 
105113eb9a7Sfgsch 	if (value > SEM_VALUE_MAX) {
1069979a4edStedu 		errno = EINVAL;
1079979a4edStedu 		return (-1);
1089979a4edStedu 	}
1099979a4edStedu 
1104afbc249Smpi 	if (pshared) {
111378aca9aStedu 		errno = EPERM;
112378aca9aStedu 		return (-1);
113378aca9aStedu #ifdef notyet
114378aca9aStedu 		char name[SEM_RANDOM_NAME_LEN];
115378aca9aStedu 		sem_t *sempshared;
116378aca9aStedu 		int i;
117378aca9aStedu 
118113eb9a7Sfgsch 		for (;;) {
1194a5ade84Stedu 			for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
1204a5ade84Stedu 				name[i] = arc4random_uniform(255) + 1;
1214a5ade84Stedu 			name[SEM_RANDOM_NAME_LEN - 1] = '\0';
122113eb9a7Sfgsch 			sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value);
123113eb9a7Sfgsch 			if (sempshared != SEM_FAILED)
1244a5ade84Stedu 				break;
1254a5ade84Stedu 			if (errno == EEXIST)
1264a5ade84Stedu 				continue;
127113eb9a7Sfgsch 			if (errno != EPERM)
1284a5ade84Stedu 				errno = ENOSPC;
1294afbc249Smpi 			return (-1);
1304afbc249Smpi 		}
1314afbc249Smpi 
1324a5ade84Stedu 		/* unnamed semaphore should not be opened twice */
1334a5ade84Stedu 		if (sem_unlink(name) == -1) {
1344a5ade84Stedu 			sem_close(sempshared);
135113eb9a7Sfgsch 			errno = ENOSPC;
1364a5ade84Stedu 			return (-1);
1374a5ade84Stedu 		}
1384a5ade84Stedu 
139113eb9a7Sfgsch 		*semp = *sempshared;
1409979a4edStedu 		free(sempshared);
1414a5ade84Stedu 		return (0);
142378aca9aStedu #endif
1434a5ade84Stedu 	}
1444a5ade84Stedu 
145349e3315Smpi 	sem = calloc(1, sizeof(*sem));
146349e3315Smpi 	if (!sem) {
147349e3315Smpi 		errno = ENOSPC;
148349e3315Smpi 		return (-1);
149349e3315Smpi 	}
1504afbc249Smpi 	sem->value = value;
1514afbc249Smpi 	*semp = sem;
1524afbc249Smpi 
1534afbc249Smpi 	return (0);
1544afbc249Smpi }
1554afbc249Smpi 
1564afbc249Smpi int
sem_destroy(sem_t * semp)1574afbc249Smpi sem_destroy(sem_t *semp)
1584afbc249Smpi {
1594a5ade84Stedu 	sem_t sem;
1604a5ade84Stedu 
161a5511fa9Sguenther 	if (!_threads_ready)		 /* for SEM_MMAP_SIZE */
162a5511fa9Sguenther 		_rthread_init();
163a5511fa9Sguenther 
1649979a4edStedu 	if (!semp || !(sem = *semp)) {
165349e3315Smpi 		errno = EINVAL;
166349e3315Smpi 		return (-1);
167349e3315Smpi 	}
168349e3315Smpi 
1694a5ade84Stedu 	if (sem->waitcount) {
1704afbc249Smpi #define MSG "sem_destroy on semaphore with waiters!\n"
1714afbc249Smpi 		write(2, MSG, sizeof(MSG) - 1);
1724afbc249Smpi #undef MSG
173349e3315Smpi 		errno = EBUSY;
174349e3315Smpi 		return (-1);
1754afbc249Smpi 	}
1764a5ade84Stedu 
1774afbc249Smpi 	*semp = NULL;
1789979a4edStedu 	if (sem->shared)
1799979a4edStedu 		munmap(sem, SEM_MMAP_SIZE);
1809979a4edStedu 	else
1814a5ade84Stedu 		free(sem);
1824afbc249Smpi 
1834afbc249Smpi 	return (0);
1844afbc249Smpi }
1854afbc249Smpi 
1864afbc249Smpi int
sem_getvalue(sem_t * semp,int * sval)1874afbc249Smpi sem_getvalue(sem_t *semp, int *sval)
1884afbc249Smpi {
1899979a4edStedu 	sem_t sem;
1904afbc249Smpi 
1919979a4edStedu 	if (!semp || !(sem = *semp)) {
192349e3315Smpi 		errno = EINVAL;
193349e3315Smpi 		return (-1);
194349e3315Smpi 	}
195349e3315Smpi 
1964afbc249Smpi 	*sval = sem->value;
1974afbc249Smpi 
1984afbc249Smpi 	return (0);
1994afbc249Smpi }
2004afbc249Smpi 
2014afbc249Smpi int
sem_post(sem_t * semp)2024afbc249Smpi sem_post(sem_t *semp)
2034afbc249Smpi {
2049979a4edStedu 	sem_t sem;
2054afbc249Smpi 
2069979a4edStedu 	if (!semp || !(sem = *semp)) {
207349e3315Smpi 		errno = EINVAL;
208349e3315Smpi 		return (-1);
209349e3315Smpi 	}
210349e3315Smpi 
2114afbc249Smpi 	_sem_post(sem);
2124afbc249Smpi 
2134afbc249Smpi 	return (0);
2144afbc249Smpi }
2154afbc249Smpi 
2164afbc249Smpi int
sem_wait(sem_t * semp)2174afbc249Smpi sem_wait(sem_t *semp)
2184afbc249Smpi {
219fe38b55cSguenther 	struct tib *tib = TIB_GET();
220fe38b55cSguenther 	pthread_t self;
2219979a4edStedu 	sem_t sem;
222ada67568Smpi 	int error;
223fe38b55cSguenther 	PREP_CANCEL_POINT(tib);
224fe38b55cSguenther 
225fe38b55cSguenther 	if (!_threads_ready)
226fe38b55cSguenther 		_rthread_init();
227fe38b55cSguenther 	self = tib->tib_thread;
2284afbc249Smpi 
2299979a4edStedu 	if (!semp || !(sem = *semp)) {
230349e3315Smpi 		errno = EINVAL;
231349e3315Smpi 		return (-1);
232349e3315Smpi 	}
233349e3315Smpi 
234fe38b55cSguenther 	ENTER_DELAYED_CANCEL_POINT(tib, self);
235ada67568Smpi 	error = _sem_wait(sem, 1, NULL, &self->delayed_cancel);
236ada67568Smpi 	LEAVE_CANCEL_POINT_INNER(tib, error);
23737ebc96dSguenther 
238ada67568Smpi 	if (error) {
239ada67568Smpi 		errno = error;
2404b11781bSpirofti 		_rthread_debug(1, "%s: v=%d errno=%d\n", __func__,
2414b11781bSpirofti 		    sem->value, errno);
24237ebc96dSguenther 		return (-1);
24337ebc96dSguenther 	}
24437ebc96dSguenther 
24537ebc96dSguenther 	return (0);
24637ebc96dSguenther }
24737ebc96dSguenther 
24837ebc96dSguenther int
sem_timedwait(sem_t * semp,const struct timespec * abstime)24937ebc96dSguenther sem_timedwait(sem_t *semp, const struct timespec *abstime)
25037ebc96dSguenther {
251fe38b55cSguenther 	struct tib *tib = TIB_GET();
252fe38b55cSguenther 	pthread_t self;
2539979a4edStedu 	sem_t sem;
254ada67568Smpi 	int error;
255fe38b55cSguenther 	PREP_CANCEL_POINT(tib);
256fe38b55cSguenther 
257*c554a768Scheloha 	if (!semp || !(sem = *semp) || !abstime || !timespecisvalid(abstime)) {
25837ebc96dSguenther 		errno = EINVAL;
25937ebc96dSguenther 		return (-1);
26037ebc96dSguenther 	}
26137ebc96dSguenther 
262bda456ccSpirofti 	if (!_threads_ready)
263bda456ccSpirofti 		_rthread_init();
264bda456ccSpirofti 	self = tib->tib_thread;
265bda456ccSpirofti 
266fe38b55cSguenther 	ENTER_DELAYED_CANCEL_POINT(tib, self);
267ada67568Smpi 	error = _sem_wait(sem, 1, abstime, &self->delayed_cancel);
268ada67568Smpi 	LEAVE_CANCEL_POINT_INNER(tib, error);
26937ebc96dSguenther 
270ada67568Smpi 	if (error) {
271ada67568Smpi 		errno = (error == EWOULDBLOCK) ? ETIMEDOUT : error;
2724b11781bSpirofti 		_rthread_debug(1, "%s: v=%d errno=%d\n", __func__,
2734b11781bSpirofti 		    sem->value, errno);
27437ebc96dSguenther 		return (-1);
27537ebc96dSguenther 	}
2764afbc249Smpi 
2774afbc249Smpi 	return (0);
2784afbc249Smpi }
2794afbc249Smpi 
2804afbc249Smpi int
sem_trywait(sem_t * semp)2814afbc249Smpi sem_trywait(sem_t *semp)
2824afbc249Smpi {
2839979a4edStedu 	sem_t sem;
284ada67568Smpi 	unsigned int val;
2854afbc249Smpi 
2869979a4edStedu 	if (!semp || !(sem = *semp)) {
287349e3315Smpi 		errno = EINVAL;
288349e3315Smpi 		return (-1);
289349e3315Smpi 	}
290349e3315Smpi 
291ada67568Smpi 	while ((val = sem->value) > 0) {
292ada67568Smpi 		if (atomic_cas_uint(&sem->value, val, val - 1) == val) {
2934b11781bSpirofti 			membar_enter_after_atomic();
2944b11781bSpirofti 			return (0);
2954b11781bSpirofti 		}
2964afbc249Smpi 	}
2974afbc249Smpi 
2984b11781bSpirofti 	errno = EAGAIN;
2994b11781bSpirofti 	_rthread_debug(1, "%s: v=%d errno=%d\n", __func__, sem->value, errno);
3004b11781bSpirofti 	return (-1);
3014afbc249Smpi }
3024afbc249Smpi 
3034a5ade84Stedu 
3044a5ade84Stedu static void
makesempath(const char * origpath,char * sempath,size_t len)3054a5ade84Stedu makesempath(const char *origpath, char *sempath, size_t len)
3064a5ade84Stedu {
3074a5ade84Stedu 	char buf[SHA256_DIGEST_STRING_LENGTH];
3084a5ade84Stedu 
3094a5ade84Stedu 	SHA256Data(origpath, strlen(origpath), buf);
3104a5ade84Stedu 	snprintf(sempath, len, "/tmp/%s.sem", buf);
3114a5ade84Stedu }
3124a5ade84Stedu 
313349e3315Smpi sem_t *
sem_open(const char * name,int oflag,...)3144a5ade84Stedu sem_open(const char *name, int oflag, ...)
315349e3315Smpi {
3164a5ade84Stedu 	char sempath[SEM_PATH_SIZE];
3174a5ade84Stedu 	struct stat sb;
318113eb9a7Sfgsch 	sem_t sem, *semp;
319113eb9a7Sfgsch 	unsigned int value = 0;
320113eb9a7Sfgsch 	int created = 0, fd;
3214a5ade84Stedu 
322fe38b55cSguenther 	if (!_threads_ready)
323fe38b55cSguenther 		_rthread_init();
324fe38b55cSguenther 
3254a5ade84Stedu 	if (oflag & ~(O_CREAT | O_EXCL)) {
3264a5ade84Stedu 		errno = EINVAL;
327113eb9a7Sfgsch 		return (SEM_FAILED);
328349e3315Smpi 	}
329349e3315Smpi 
330a5de71bfStedu 	if (oflag & O_CREAT) {
331a5de71bfStedu 		va_list ap;
332a5de71bfStedu 		va_start(ap, oflag);
333113eb9a7Sfgsch 		/* 3rd parameter mode is not used */
334113eb9a7Sfgsch 		va_arg(ap, mode_t);
335a5de71bfStedu 		value = va_arg(ap, unsigned);
336a5de71bfStedu 		va_end(ap);
337113eb9a7Sfgsch 
338113eb9a7Sfgsch 		if (value > SEM_VALUE_MAX) {
339113eb9a7Sfgsch 			errno = EINVAL;
340113eb9a7Sfgsch 			return (SEM_FAILED);
341113eb9a7Sfgsch 		}
342a5de71bfStedu 	}
343a5de71bfStedu 
3444a5ade84Stedu 	makesempath(name, sempath, sizeof(sempath));
3454a5ade84Stedu 	fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600);
3464a5ade84Stedu 	if (fd == -1)
347113eb9a7Sfgsch 		return (SEM_FAILED);
3484a5ade84Stedu 	if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
3494a5ade84Stedu 		close(fd);
3504a5ade84Stedu 		errno = EINVAL;
351113eb9a7Sfgsch 		return (SEM_FAILED);
3524a5ade84Stedu 	}
35359e6ef93Stedu 	if (sb.st_uid != geteuid()) {
3544a5ade84Stedu 		close(fd);
3554a5ade84Stedu 		errno = EPERM;
356113eb9a7Sfgsch 		return (SEM_FAILED);
3574a5ade84Stedu 	}
3587567a0bfSguenther 	if (sb.st_size != (off_t)SEM_MMAP_SIZE) {
3599979a4edStedu 		if (!(oflag & O_CREAT)) {
3609979a4edStedu 			close(fd);
3619979a4edStedu 			errno = EINVAL;
362113eb9a7Sfgsch 			return (SEM_FAILED);
3639979a4edStedu 		}
3649979a4edStedu 		if (sb.st_size != 0) {
3659979a4edStedu 			close(fd);
3669979a4edStedu 			errno = EINVAL;
367113eb9a7Sfgsch 			return (SEM_FAILED);
3689979a4edStedu 		}
3694a5ade84Stedu 		if (ftruncate(fd, SEM_MMAP_SIZE) == -1) {
3704a5ade84Stedu 			close(fd);
371113eb9a7Sfgsch 			errno = EINVAL;
372113eb9a7Sfgsch 			return (SEM_FAILED);
373e156381bStedu 		}
374113eb9a7Sfgsch 
3759979a4edStedu 		created = 1;
3764a5ade84Stedu 	}
3779979a4edStedu 	sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE,
3785a78b117Smatthew 	    MAP_SHARED, fd, 0);
3794a5ade84Stedu 	close(fd);
3809979a4edStedu 	if (sem == MAP_FAILED) {
381113eb9a7Sfgsch 		errno = EINVAL;
382113eb9a7Sfgsch 		return (SEM_FAILED);
383113eb9a7Sfgsch 	}
384113eb9a7Sfgsch 	semp = malloc(sizeof(*semp));
385113eb9a7Sfgsch 	if (!semp) {
386113eb9a7Sfgsch 		munmap(sem, SEM_MMAP_SIZE);
387113eb9a7Sfgsch 		errno = ENOSPC;
388113eb9a7Sfgsch 		return (SEM_FAILED);
3894a5ade84Stedu 	}
390a5de71bfStedu 	if (created) {
391a5de71bfStedu 		sem->value = value;
3929979a4edStedu 		sem->shared = 1;
3934a5ade84Stedu 	}
3944a5ade84Stedu 	*semp = sem;
3954a5ade84Stedu 
396e156381bStedu 	return (semp);
397349e3315Smpi }
398349e3315Smpi 
399349e3315Smpi int
sem_close(sem_t * semp)4004a5ade84Stedu sem_close(sem_t *semp)
401349e3315Smpi {
4029979a4edStedu 	sem_t sem;
4034a5ade84Stedu 
4049979a4edStedu 	if (!semp || !(sem = *semp) || !sem->shared) {
4059979a4edStedu 		errno = EINVAL;
4069979a4edStedu 		return (-1);
4079979a4edStedu 	}
4089979a4edStedu 
409113eb9a7Sfgsch 	*semp = NULL;
4109979a4edStedu 	munmap(sem, SEM_MMAP_SIZE);
4114a5ade84Stedu 	free(semp);
4124a5ade84Stedu 
4134a5ade84Stedu 	return (0);
4144a5ade84Stedu }
4154a5ade84Stedu 
4164a5ade84Stedu int
sem_unlink(const char * name)4174a5ade84Stedu sem_unlink(const char *name)
4184a5ade84Stedu {
4194a5ade84Stedu 	char sempath[SEM_PATH_SIZE];
4204a5ade84Stedu 
4214a5ade84Stedu 	makesempath(name, sempath, sizeof(sempath));
422e156381bStedu 	return (unlink(sempath));
423349e3315Smpi }
424