> This may or may not be a working implementation of sem_open.

This is a bit improved version of Ted's diff:

  * Add support of shared=1 for sem_init()
  * Less memory leaks in error paths
  * More strong checks where applicable

This is RFC/FYI status only, still under testing.
--
zhuk@


Index: sys/kern/kern_synch.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_synch.c,v
retrieving revision 1.108
diff -u -p -r1.108 kern_synch.c
--- sys/kern/kern_synch.c       14 Sep 2013 01:35:01 -0000      1.108
+++ sys/kern/kern_synch.c       26 Oct 2013 01:34:03 -0000
@@ -419,6 +419,7 @@ thrsleep_unlock(void *lock, int lockflag
        return (error);
 }
 
+static int magicnumber;
 
 int
 thrsleep(struct proc *p, struct sys___thrsleep_args *v)
@@ -482,9 +483,13 @@ thrsleep(struct proc *p, struct sys___th
 
        if (p->p_thrslpid == 0)
                error = 0;
-       else
-               error = tsleep(&p->p_thrslpid, PUSER | PCATCH, "thrsleep",
+       else {
+               void *sleepaddr = &p->p_thrslpid;
+               if (ident == -1)
+                       sleepaddr = &magicnumber;
+               error = tsleep(sleepaddr, PUSER | PCATCH, "thrsleep",
                    (int)to_ticks);
+       }
 
 out:
        p->p_thrslpid = 0;
@@ -535,6 +540,8 @@ sys___thrwakeup(struct proc *p, void *v,
 
        if (ident == 0)
                *retval = EINVAL;
+       else if (ident == -1)
+               wakeup(&magicnumber);
        else {
                TAILQ_FOREACH(q, &p->p_p->ps_threads, p_thr_link) {
                        if (q->p_thrslpid == ident) {
Index: lib/librthread/rthread.h
===================================================================
RCS file: /cvs/src/lib/librthread/rthread.h,v
retrieving revision 1.45
diff -u -p -r1.45 rthread.h
--- lib/librthread/rthread.h    21 Jun 2013 06:08:50 -0000      1.45
+++ lib/librthread/rthread.h    26 Oct 2013 01:34:03 -0000
@@ -64,7 +64,7 @@ struct __sem {
        struct _spinlock lock;
        volatile int waitcount;
        volatile int value;
-       int __pad;
+       struct __sem *shared;
 };
 
 TAILQ_HEAD(pthread_queue, pthread);
Index: lib/librthread/rthread_sem.c
===================================================================
RCS file: /cvs/src/lib/librthread/rthread_sem.c,v
retrieving revision 1.9
diff -u -p -r1.9 rthread_sem.c
--- lib/librthread/rthread_sem.c        1 Jun 2013 23:06:26 -0000       1.9
+++ lib/librthread/rthread_sem.c        26 Oct 2013 01:34:03 -0000
@@ -1,6 +1,6 @@
 /*     $OpenBSD: rthread_sem.c,v 1.9 2013/06/01 23:06:26 tedu Exp $ */
 /*
- * Copyright (c) 2004,2005 Ted Unangst <[email protected]>
+ * Copyright (c) 2004,2005,2013 Ted Unangst <[email protected]>
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -15,15 +15,39 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
 
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sha2.h>
 #include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
 #include <unistd.h>
-#include <errno.h>
 
 #include <pthread.h>
 
 #include "rthread.h"
 
+#define SHARED_IDENT ((void *)-1)
+
+/* SHA256_DIGEST_STRING_LENGTH includes nul */
+/* "/tmp/" + sha256 + ".sem" */
+#define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4)
+
+/* long enough to be hard to guess */
+#define SEM_RANDOM_NAME_LEN    160
+
+/*
+ * Size of memory to be mmap()'ed by named semaphores.
+ * Should be >= SEM_PATH_SIZE and page-aligned.
+ */
+#define SEM_MMAP_SIZE  PAGE_SIZE
+
 /*
  * Internal implementation of semaphores
  */
@@ -31,8 +55,14 @@ int
 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime,
     int *delayed_cancel)
 {
+       void *ident = (void *)&sem->waitcount;
        int r;
 
+       if (sem->shared) {
+               sem = sem->shared;
+               ident = SHARED_IDENT;
+       }
+
        _spinlock(&sem->lock);
        if (sem->value) {
                sem->value--;
@@ -42,7 +72,7 @@ _sem_wait(sem_t sem, int tryonly, const 
        } else {
                sem->waitcount++;
                do {
-                       r = __thrsleep(&sem->waitcount, CLOCK_REALTIME |
+                       r = __thrsleep(ident, CLOCK_REALTIME |
                            _USING_TICKETS, abstime, &sem->lock.ticket,
                            delayed_cancel);
                        _spinlock(&sem->lock);
@@ -63,12 +93,18 @@ _sem_wait(sem_t sem, int tryonly, const 
 int
 _sem_post(sem_t sem)
 {
+       void *ident = (void *)&sem->waitcount;
        int rv = 0;
 
+       if (sem->shared) {
+               sem = sem->shared;
+               ident = SHARED_IDENT;
+       }
+
        _spinlock(&sem->lock);
        sem->value++;
        if (sem->waitcount) {
-               __thrwakeup(&sem->waitcount, 1);
+               __thrwakeup(ident, 1);
                rv = 1;
        }
        _spinunlock(&sem->lock);
@@ -81,11 +117,39 @@ _sem_post(sem_t sem)
 int
 sem_init(sem_t *semp, int pshared, unsigned int value)
 {
-       sem_t sem;
+       sem_t sem, *sempshared;
+       int i, oerrno;
+       char name[SEM_RANDOM_NAME_LEN];    
 
        if (pshared) {
-               errno = EPERM;
-               return (-1);
+               while (1) {
+                       for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
+                               name[i] = arc4random_uniform(255) + 1;
+                       name[SEM_RANDOM_NAME_LEN - 1] = '\0';
+                       sempshared = sem_open(name, O_CREAT|O_EXCL);
+                       if (sempshared) {
+                               break;
+                       }
+                       if (errno == EEXIST)
+                               continue;
+                       if (errno != EINVAL && errno != EPERM)
+                               errno = ENOSPC;
+                       return (-1);
+               }
+
+               /* unnamed semaphore should not be opened twice */
+               if (sem_unlink(name) == -1) {
+                       oerrno = errno;
+                       sem_close(sempshared);
+                       errno = oerrno;
+                       return (-1);
+               }
+
+               sem = *sempshared;
+               free(sempshared);
+               sem->value = value;
+               *semp = sem;
+               return (0);
        }
 
        if (value > SEM_VALUE_MAX) {
@@ -108,20 +172,30 @@ sem_init(sem_t *semp, int pshared, unsig
 int
 sem_destroy(sem_t *semp)
 {
+       sem_t sem;
+
        if (!semp || !*semp) {
                errno = EINVAL;
                return (-1);
        }
+       sem = *semp;
 
-       if ((*semp)->waitcount) {
+       if (sem->waitcount) {
 #define MSG "sem_destroy on semaphore with waiters!\n"
                write(2, MSG, sizeof(MSG) - 1);
 #undef MSG
                errno = EBUSY;
                return (-1);
        }
-       free(*semp);
+
+       if (sem->shared)
+               if (munmap(sem->shared, SEM_MMAP_SIZE) == -1) {
+                       warn("sem_destroy: someone borked shared memory");
+                       abort();
+               }
+
        *semp = NULL;
+       free(sem);
 
        return (0);
 }
@@ -136,6 +210,9 @@ sem_getvalue(sem_t *semp, int *sval)
                return (-1);
        }
 
+       if (sem->shared)
+               sem = sem->shared;
+
        _spinlock(&sem->lock);
        *sval = sem->value;
        _spinunlock(&sem->lock);
@@ -227,27 +304,96 @@ sem_trywait(sem_t *semp)
        return (0);
 }
 
-/* ARGSUSED */
+
+static void
+makesempath(const char *origpath, char *sempath, size_t len)
+{
+       char buf[SHA256_DIGEST_STRING_LENGTH];
+
+       SHA256Data(origpath, strlen(origpath), buf);
+       snprintf(sempath, len, "/tmp/%s.sem", buf);
+}
+
 sem_t *
-sem_open(const char *name __unused, int oflag __unused, ...)
+sem_open(const char *name, int oflag, ...)
 {
-       errno = ENOSYS;
-       return (SEM_FAILED);
+       char sempath[SEM_PATH_SIZE];
+       struct stat sb;
+       int fd, oerrno;
+       sem_t sem, shared;
+       sem_t *semp = SEM_FAILED;
+
+       if (oflag & ~(O_CREAT | O_EXCL)) {
+               errno = EINVAL;
+               return semp;
+       }
+
+       makesempath(name, sempath, sizeof(sempath));
+       fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600);
+       if (fd == -1)
+               return semp;
+       if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+               close(fd);
+               errno = EINVAL;
+               return semp;
+       }
+       if (sb.st_uid != getuid()) {
+               close(fd);
+               errno = EPERM;
+               return semp;
+       }
+       if (sb.st_size < SEM_MMAP_SIZE)
+               if (ftruncate(fd, SEM_MMAP_SIZE) == -1) {
+                       oerrno = errno;
+                       close(fd);
+                       errno = oerrno;
+                       /* XXX can set errno to EIO, ENOTDIR... */
+                       return semp;
+               }
+       shared = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE,
+           MAP_FILE | MAP_SHARED | MAP_HASSEMAPHORE, fd, 0);
+       oerrno = errno;
+       close(fd);
+       if (shared == MAP_FAILED) {
+               errno = oerrno;
+               return semp;
+       }
+       semp = malloc(sizeof(*semp));
+       sem = calloc(1, sizeof(*sem));
+       if (!semp || !sem) {
+               free(sem);
+               free(semp);
+               munmap(shared, SEM_MMAP_SIZE);
+               errno = ENOSPC;
+               return SEM_FAILED;
+       }
+       *semp = sem;
+       sem->shared = shared;
+
+       return semp;
 }
 
-/* ARGSUSED */
 int
-sem_close(sem_t *sem __unused)
+sem_close(sem_t *semp)
 {
-       errno = ENOSYS;
-       return (-1);
+       sem_t sem = *semp;
+
+       if (munmap(sem->shared, SEM_MMAP_SIZE) == -1) {
+               warn("sem_close: someone borked shared memory");
+               abort();
+       }
+       free(sem);
+       free(semp);
+
+       return (0);
 }
 
-/* ARGSUSED */
 int
-sem_unlink(const char *name __unused)
+sem_unlink(const char *name)
 {
-       errno = ENOSYS;
-       return (-1);
+       char sempath[SEM_PATH_SIZE];
+
+       makesempath(name, sempath, sizeof(sempath));
+       return unlink(sempath);
 }
 

Reply via email to