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