Module Name: src Committed By: thorpej Date: Sun Feb 3 03:20:24 UTC 2019
Modified Files: src/distrib/sets/lists/tests: mi src/lib/libpthread: Makefile src/lib/librt: sem.c src/sys/compat/netbsd32: netbsd32_sem.c src/sys/kern: uipc_sem.c src/sys/sys: ksem.h src/tests/kernel: Makefile src/tests/lib/librt: t_sem.c src/usr.bin/fstat: misc.c Added Files: src/tests/kernel: t_ksem.c Removed Files: src/lib/libpthread: sem.c Log Message: Implement support for "pshared" POSIX semaphores. Fixes lib/53273 (and Firefox's multi-process tab feature). To generate a diff of this commit: cvs rdiff -u -r1.804 -r1.805 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.90 -r1.91 src/lib/libpthread/Makefile cvs rdiff -u -r1.24 -r0 src/lib/libpthread/sem.c cvs rdiff -u -r1.7 -r1.8 src/lib/librt/sem.c cvs rdiff -u -r1.11 -r1.12 src/sys/compat/netbsd32/netbsd32_sem.c cvs rdiff -u -r1.51 -r1.52 src/sys/kern/uipc_sem.c cvs rdiff -u -r1.14 -r1.15 src/sys/sys/ksem.h cvs rdiff -u -r1.56 -r1.57 src/tests/kernel/Makefile cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_ksem.c cvs rdiff -u -r1.3 -r1.4 src/tests/lib/librt/t_sem.c cvs rdiff -u -r1.20 -r1.21 src/usr.bin/fstat/misc.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.804 src/distrib/sets/lists/tests/mi:1.805 --- src/distrib/sets/lists/tests/mi:1.804 Fri Jan 25 18:33:58 2019 +++ src/distrib/sets/lists/tests/mi Sun Feb 3 03:20:23 2019 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.804 2019/01/25 18:33:58 christos Exp $ +# $NetBSD: mi,v 1.805 2019/02/03 03:20:23 thorpej Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2180,6 +2180,7 @@ ./usr/tests/kernel/t_filedesc tests-kernel-tests atf,rump ./usr/tests/kernel/t_interp tests-kernel-tests atf ./usr/tests/kernel/t_kauth_pr_47598 tests-kernel-tests compattestfile,atf +./usr/tests/kernel/t_ksem tests-kernel-tests atf ./usr/tests/kernel/t_lock tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_lockf tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_lwpctl tests-obsolete obsolete Index: src/lib/libpthread/Makefile diff -u src/lib/libpthread/Makefile:1.90 src/lib/libpthread/Makefile:1.91 --- src/lib/libpthread/Makefile:1.90 Sat Jun 9 23:45:56 2018 +++ src/lib/libpthread/Makefile Sun Feb 3 03:20:24 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.90 2018/06/09 23:45:56 christos Exp $ +# $NetBSD: Makefile,v 1.91 2019/02/03 03:20:24 thorpej Exp $ # NOSANITIZER= # defined @@ -66,6 +66,7 @@ SRCS+= pthread_specific.c SRCS+= pthread_spin.c SRCS+= pthread_tsd.c SRCS+= res_state.c +.PATH: ${.CURDIR}/../librt SRCS+= sem.c # Architecture-dependent files .if exists(${ARCHDIR}/pthread_md.S) Index: src/lib/librt/sem.c diff -u src/lib/librt/sem.c:1.7 src/lib/librt/sem.c:1.8 --- src/lib/librt/sem.c:1.7 Sat Mar 10 19:59:21 2012 +++ src/lib/librt/sem.c Sun Feb 3 03:20:24 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg Exp $ */ +/* $NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $ */ /*- - * Copyright (c) 2003 The NetBSD Foundation, Inc. + * Copyright (c) 2003, 2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -59,12 +59,18 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg Exp $"); +__RCSID("$NetBSD: sem.c,v 1.8 2019/02/03 03:20:24 thorpej Exp $"); +#ifndef __LIBPTHREAD_SOURCE__ /* - * If an application is linked against both librt and libpthread, the - * libpthread versions must be used. Provide weak aliases to cause - * this behavior. + * There is no longer any difference between the libpthread and the librt + * versions of sem.c; both are fully kernel-assisted via the _ksem_*() + * system calls. The only difference is the need to lock some internal + * data structures in the pthread version, which could be achieved by + * different means. However, in order to maintain binary compatibility + * with applications that use POSIX semaphores and linked against only + * libpthread, we continue to maintain a copy of the implementation here + * that does not depend on any additional libraries (other than libc). */ #define sem_init _librt_sem_init #define sem_destroy _librt_sem_destroy @@ -76,7 +82,9 @@ __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 #define sem_trywait _librt_sem_trywait #define sem_post _librt_sem_post #define sem_getvalue _librt_sem_getvalue +#endif /* ! __LIBPTHREAD_SOURCE__ */ +#undef _LIBC #define _LIBC #include <sys/types.h> @@ -88,20 +96,47 @@ __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 #include <semaphore.h> #include <stdarg.h> +#ifdef __LIBPTHREAD_SOURCE__ +#include "pthread.h" +#endif /* __LIBPTHREAD_SOURCE__ */ + +#define SEM_NAMED 0x4e414d44U /* 'NAMD' */ +#define SEM_MAGIC 0x90af0421U +#define SEM_MAGIC_NAMED (SEM_MAGIC ^ SEM_NAMED) + +#define SEM_IS_KSEMID(k) ((((intptr_t)(k)) & KSEM_MARKER_MASK) \ + == KSEM_PSHARED_MARKER) + +#define SEM_IS_UNNAMED(k) (SEM_IS_KSEMID(k) || \ + (k)->ksem_magic == SEM_MAGIC) + +#define SEM_IS_NAMED(k) (!SEM_IS_UNNAMED(k)) + +#define SEM_MAGIC_OK(k) (SEM_IS_KSEMID(k) || \ + (k)->ksem_magic == SEM_MAGIC || \ + (k)->ksem_magic == SEM_MAGIC_NAMED) + struct _sem_st { unsigned int ksem_magic; -#define KSEM_MAGIC 0x90af0421U + intptr_t ksem_semid; + /* Used only to de-dup named semaphores. */ LIST_ENTRY(_sem_st) ksem_list; - intptr_t ksem_semid; /* 0 -> user (non-shared) */ sem_t *ksem_identity; }; -static int sem_alloc(unsigned int value, intptr_t semid, sem_t *semp); -static void sem_free(sem_t sem); - static LIST_HEAD(, _sem_st) named_sems = LIST_HEAD_INITIALIZER(&named_sems); +#ifdef __LIBPTHREAD_SOURCE__ +static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_NAMED_SEMS() pthread_mutex_lock(&named_sems_mtx) +#define UNLOCK_NAMED_SEMS() pthread_mutex_unlock(&named_sems_mtx) +#else /* ! __LIBPTHREAD_SOURCE__ */ +#define LOCK_NAMED_SEMS() __nothing +#define UNLOCK_NAMED_SEMS() __nothing +#endif /* __LIBPTHREAD_SOURCE__ */ + +#ifndef __LIBPTHREAD_SOURCE__ #ifdef __weak_alias __weak_alias(sem_init,_librt_sem_init) __weak_alias(sem_destroy,_librt_sem_destroy) @@ -113,7 +148,20 @@ __weak_alias(sem_timedwait,_librt_sem_ti __weak_alias(sem_trywait,_librt_sem_trywait) __weak_alias(sem_post,_librt_sem_post) __weak_alias(sem_getvalue,_librt_sem_getvalue) -#endif +#else +#error Weak aliases required to build POSIX semaphore support. +#endif /* __weak_alias */ +#endif /* __LIBPTHREAD_SOURCE__ */ + +static inline intptr_t +sem_to_semid(sem_t *sem) +{ + + if (SEM_IS_KSEMID(*sem)) + return (intptr_t)*sem; + + return (*sem)->ksem_semid; +} static void sem_free(sem_t sem) @@ -124,7 +172,7 @@ sem_free(sem_t sem) } static int -sem_alloc(unsigned int value, intptr_t semid, sem_t *semp) +sem_alloc(unsigned int value, intptr_t semid, unsigned int magic, sem_t *semp) { sem_t sem; @@ -134,7 +182,7 @@ sem_alloc(unsigned int value, intptr_t s if ((sem = malloc(sizeof(struct _sem_st))) == NULL) return (ENOSPC); - sem->ksem_magic = KSEM_MAGIC; + sem->ksem_magic = magic; sem->ksem_semid = semid; *semp = sem; @@ -145,13 +193,36 @@ sem_alloc(unsigned int value, intptr_t s int sem_init(sem_t *sem, int pshared, unsigned int value) { - intptr_t semid; + intptr_t semid = pshared ? KSEM_PSHARED : 0; int error; if (_ksem_init(value, &semid) == -1) return (-1); - if ((error = sem_alloc(value, semid, sem)) != 0) { + /* + * pshared anonymous semaphores are treated a little differently. + * We don't allocate a sem structure and return a pointer to it. + * That pointer might live in the shared memory segment that's + * shared between processes, but the _sem_st that contains the + * important bits certainly would not be. + * + * So, instead, we return the ksem ID given to us by the kernel. + * The kernel has arranged for the least-significant bit of the + * ksem ID to always be 1 so as to ensure we can always tell + * these IDs apart from the pointers that we vend out for other + * non-pshared semaphores. + */ + if (pshared) { + if ((semid & KSEM_MARKER_MASK) != KSEM_PSHARED_MARKER) { + _ksem_destroy(semid); + errno = ENOTSUP; + return (-1); + } + *sem = (sem_t)semid; + return (0); + } + + if ((error = sem_alloc(value, semid, SEM_MAGIC, sem)) != 0) { _ksem_destroy(semid); errno = error; return (-1); @@ -166,16 +237,25 @@ sem_destroy(sem_t *sem) int error, save_errno; #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - error = _ksem_destroy((*sem)->ksem_semid); - save_errno = errno; - sem_free(*sem); - errno = save_errno; + if (SEM_IS_KSEMID(*sem)) { + error = _ksem_destroy((intptr_t)*sem); + } else { + if (SEM_IS_NAMED(*sem)) { + errno = EINVAL; + return (-1); + } + + error = _ksem_destroy((*sem)->ksem_semid); + save_errno = errno; + sem_free(*sem); + errno = save_errno; + } return error; } @@ -211,24 +291,29 @@ sem_open(const char *name, int oflag, .. * Search for a duplicate ID, we must return the same sem_t * * if we locate one. */ + LOCK_NAMED_SEMS(); LIST_FOREACH(s, &named_sems, ksem_list) { - if (s->ksem_semid == semid) + if (s->ksem_semid == semid) { + UNLOCK_NAMED_SEMS(); return (s->ksem_identity); + } } if ((sem = malloc(sizeof(*sem))) == NULL) { error = ENOSPC; goto bad; } - if ((error = sem_alloc(value, semid, sem)) != 0) + if ((error = sem_alloc(value, semid, SEM_MAGIC_NAMED, sem)) != 0) goto bad; LIST_INSERT_HEAD(&named_sems, *sem, ksem_list); + UNLOCK_NAMED_SEMS(); (*sem)->ksem_identity = sem; return (sem); bad: + UNLOCK_NAMED_SEMS(); _ksem_close(semid); if (sem != NULL) { if (*sem != NULL) @@ -245,16 +330,22 @@ sem_close(sem_t *sem) int error, save_errno; #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - error = _ksem_close((*sem)->ksem_semid); + if (!SEM_IS_NAMED(*sem)) { + errno = EINVAL; + return (-1); + } + LOCK_NAMED_SEMS(); + error = _ksem_close((*sem)->ksem_semid); LIST_REMOVE((*sem), ksem_list); save_errno = errno; + UNLOCK_NAMED_SEMS(); sem_free(*sem); free(sem); errno = save_errno; @@ -273,13 +364,13 @@ sem_wait(sem_t *sem) { #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - return (_ksem_wait((*sem)->ksem_semid)); + return (_ksem_wait(sem_to_semid(sem))); } int @@ -287,13 +378,13 @@ sem_timedwait(sem_t *sem, const struct t { #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - return (_ksem_timedwait((*sem)->ksem_semid, abstime)); + return (_ksem_timedwait(sem_to_semid(sem), abstime)); } int @@ -301,13 +392,13 @@ sem_trywait(sem_t *sem) { #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - return (_ksem_trywait((*sem)->ksem_semid)); + return (_ksem_trywait(sem_to_semid(sem))); } int @@ -315,13 +406,13 @@ sem_post(sem_t *sem) { #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - return (_ksem_post((*sem)->ksem_semid)); + return (_ksem_post(sem_to_semid(sem))); } int @@ -329,10 +420,11 @@ sem_getvalue(sem_t * __restrict sem, int { #ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { + if (sem == NULL || *sem == NULL || !SEM_MAGIC_OK(*sem)) { errno = EINVAL; return (-1); } #endif - return (_ksem_getvalue((*sem)->ksem_semid, sval)); + + return (_ksem_getvalue(sem_to_semid(sem), sval)); } Index: src/sys/compat/netbsd32/netbsd32_sem.c diff -u src/sys/compat/netbsd32/netbsd32_sem.c:1.11 src/sys/compat/netbsd32/netbsd32_sem.c:1.12 --- src/sys/compat/netbsd32/netbsd32_sem.c:1.11 Fri Sep 19 17:25:33 2014 +++ src/sys/compat/netbsd32/netbsd32_sem.c Sun Feb 3 03:20:23 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_sem.c,v 1.11 2014/09/19 17:25:33 matt Exp $ */ +/* $NetBSD: netbsd32_sem.c,v 1.12 2019/02/03 03:20:23 thorpej Exp $ */ /* * Copyright (c) 2006 The NetBSD Foundation. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: netbsd32_sem.c,v 1.11 2014/09/19 17:25:33 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_sem.c,v 1.12 2019/02/03 03:20:23 thorpej Exp $"); #include <sys/types.h> #include <sys/param.h> @@ -45,6 +45,21 @@ __KERNEL_RCSID(0, "$NetBSD: netbsd32_sem #include <compat/netbsd32/netbsd32_conv.h> static int +netbsd32_ksem_copyin(const void *src, void *dst, size_t size) +{ + intptr_t *argp = dst; + netbsd32_intptr_t arg32; + int error; + + KASSERT(size == sizeof(intptr_t)); + + if ((error = copyin(src, &arg32, sizeof(arg32))) != 0) + return error; + *argp = arg32; + return 0; +} + +static int netbsd32_ksem_copyout(const void *src, void *dst, size_t size) { const intptr_t *idp = src; @@ -53,6 +68,7 @@ netbsd32_ksem_copyout(const void *src, v KASSERT(size == sizeof(intptr_t)); /* Returning a kernel pointer to userspace sucks badly :-( */ + /* (Luckily, it's not actually a pointer. --thorpej */ id32 = (netbsd32_intptr_t)*idp; return copyout(&id32, outidp, sizeof(id32)); } @@ -66,7 +82,7 @@ netbsd32__ksem_init(struct lwp *l, const } */ return do_ksem_init(l, SCARG(uap, value), - SCARG_P32(uap, idp), netbsd32_ksem_copyout); + SCARG_P32(uap, idp), netbsd32_ksem_copyin, netbsd32_ksem_copyout); } int Index: src/sys/kern/uipc_sem.c diff -u src/sys/kern/uipc_sem.c:1.51 src/sys/kern/uipc_sem.c:1.52 --- src/sys/kern/uipc_sem.c:1.51 Sun May 6 00:46:09 2018 +++ src/sys/kern/uipc_sem.c Sun Feb 3 03:20:23 2019 @@ -1,11 +1,11 @@ -/* $NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $ */ +/* $NetBSD: uipc_sem.c,v 1.52 2019/02/03 03:20:23 thorpej Exp $ */ /*- - * Copyright (c) 2011 The NetBSD Foundation, Inc. + * Copyright (c) 2011, 2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Mindaugas Rasiukevicius. + * by Mindaugas Rasiukevicius and Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -60,13 +60,14 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.52 2019/02/03 03:20:23 thorpej Exp $"); #include <sys/param.h> #include <sys/kernel.h> #include <sys/atomic.h> #include <sys/proc.h> +#include <sys/lwp.h> #include <sys/ksem.h> #include <sys/syscall.h> #include <sys/stat.h> @@ -77,11 +78,14 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v #include <sys/kauth.h> #include <sys/module.h> #include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/rwlock.h> #include <sys/semaphore.h> #include <sys/syscall.h> #include <sys/syscallargs.h> #include <sys/syscallvar.h> #include <sys/sysctl.h> +#include <sys/cprng.h> MODULE(MODULE_CLASS_MISC, ksem, NULL); @@ -95,6 +99,12 @@ static LIST_HEAD(,ksem) ksem_head __cach static u_int nsems_total __cacheline_aligned; static u_int nsems __cacheline_aligned; +static krwlock_t ksem_pshared_lock __cacheline_aligned; +static LIST_HEAD(, ksem) *ksem_pshared_hashtab __cacheline_aligned; +static u_long ksem_pshared_hashmask __read_mostly; + +#define KSEM_PSHARED_HASHSIZE 32 + static kauth_listener_t ksem_listener; static int ksem_sysinit(void); @@ -189,6 +199,11 @@ ksem_sysinit(void) nsems_total = 0; nsems = 0; + rw_init(&ksem_pshared_lock); + ksem_pshared_hashtab = hashinit(KSEM_PSHARED_HASHSIZE, HASH_LIST, + true, &ksem_pshared_hashmask); + KASSERT(ksem_pshared_hashtab != NULL); + error = syscall_establish(NULL, ksem_syscalls); if (error) { (void)ksem_sysfini(false); @@ -245,6 +260,8 @@ ksem_sysfini(bool interface) } } kauth_unlisten_scope(ksem_listener); + hashdone(ksem_pshared_hashtab, HASH_LIST, ksem_pshared_hashmask); + rw_destroy(&ksem_pshared_lock); mutex_destroy(&ksem_lock); sysctl_teardown(&ksem_clog); return 0; @@ -296,28 +313,121 @@ ksem_perm(lwp_t *l, ksem_t *ks) } /* + * Bits 1..23 are random, just pluck a few of those and assume the + * distribution is going to be pretty good. + */ +#define KSEM_PSHARED_HASH(id) (((id) >> 1) & ksem_pshared_hashmask) + +static void +ksem_remove_pshared(ksem_t *ksem) +{ + rw_enter(&ksem_pshared_lock, RW_WRITER); + LIST_REMOVE(ksem, ks_entry); + rw_exit(&ksem_pshared_lock); +} + +static ksem_t * +ksem_lookup_pshared_locked(intptr_t id) +{ + u_long bucket = KSEM_PSHARED_HASH(id); + ksem_t *ksem = NULL; + + /* ksem_t is locked and referenced upon return. */ + + LIST_FOREACH(ksem, &ksem_pshared_hashtab[bucket], ks_entry) { + if (ksem->ks_pshared_id == id) { + mutex_enter(&ksem->ks_lock); + if (ksem->ks_pshared_proc == NULL) { + /* + * This entry is dead, and in the process + * of being torn down; skip it. + */ + mutex_exit(&ksem->ks_lock); + continue; + } + ksem->ks_ref++; + KASSERT(ksem->ks_ref != 0); + return ksem; + } + } + + return NULL; +} + +static ksem_t * +ksem_lookup_pshared(intptr_t id) +{ + rw_enter(&ksem_pshared_lock, RW_READER); + ksem_t *ksem = ksem_lookup_pshared_locked(id); + rw_exit(&ksem_pshared_lock); + return ksem; +} + +static void +ksem_alloc_pshared_id(ksem_t *ksem) +{ + uint32_t try; + + KASSERT(ksem->ks_pshared_proc != NULL); + + rw_enter(&ksem_pshared_lock, RW_WRITER); + for (;;) { + try = (cprng_fast32() & ~KSEM_MARKER_MASK) | + KSEM_PSHARED_MARKER; + + if (ksem_lookup_pshared_locked(try) == NULL) { + /* Got it! */ + break; + } + } + ksem->ks_pshared_id = try; + u_long bucket = KSEM_PSHARED_HASH(ksem->ks_pshared_id); + LIST_INSERT_HEAD(&ksem_pshared_hashtab[bucket], ksem, ks_entry); + rw_exit(&ksem_pshared_lock); +} + +/* * ksem_get: get the semaphore from the descriptor. * - * => locks the semaphore, if found. + * => locks the semaphore, if found, and holds an extra reference. * => holds a reference on the file descriptor. */ static int -ksem_get(int fd, ksem_t **ksret) +ksem_get(intptr_t id, ksem_t **ksret, int *fdp) { ksem_t *ks; - file_t *fp; + int fd; - fp = fd_getfile(fd); - if (__predict_false(fp == NULL)) - return EINVAL; - if (__predict_false(fp->f_type != DTYPE_SEM)) { - fd_putfile(fd); + if ((id & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER) { + /* + * ksem_lookup_pshared() returns the ksem_t * + * locked and referenced. + */ + ks = ksem_lookup_pshared(id); + if (ks == NULL) + return EINVAL; + KASSERT(ks->ks_pshared_id == id); + KASSERT(ks->ks_pshared_proc != NULL); + fd = -1; + } else if (id <= INT_MAX) { + fd = (int)id; + file_t *fp = fd_getfile(fd); + + if (__predict_false(fp == NULL)) + return EINVAL; + if (__predict_false(fp->f_type != DTYPE_SEM)) { + fd_putfile(fd); + return EINVAL; + } + ks = fp->f_ksem; + mutex_enter(&ks->ks_lock); + ks->ks_ref++; + } else { return EINVAL; } - ks = fp->f_ksem; - mutex_enter(&ks->ks_lock); *ksret = ks; + *fdp = fd; return 0; } @@ -388,6 +498,10 @@ ksem_free(ksem_t *ks) KASSERT(!cv_has_waiters(&ks->ks_cv)); + if (ks->ks_pshared_id) { + KASSERT(ks->ks_pshared_proc == NULL); + ksem_remove_pshared(ks); + } if (ks->ks_name) { KASSERT(ks->ks_namelen > 0); kmem_free(ks->ks_name, ks->ks_namelen); @@ -400,6 +514,35 @@ ksem_free(ksem_t *ks) atomic_dec_uint(&curproc->p_nsems); } +#define KSEM_ID_IS_PSHARED(id) \ + (((id) & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER) + +static void +ksem_release(ksem_t *ksem, int fd) +{ + bool destroy = false; + + KASSERT(mutex_owned(&ksem->ks_lock)); + + KASSERT(ksem->ks_ref > 0); + if (--ksem->ks_ref == 0) { + /* + * Destroy if the last reference and semaphore is unnamed, + * or unlinked (for named semaphore). + */ + destroy = (ksem->ks_flags & KS_UNLINKED) || + (ksem->ks_name == NULL); + } + mutex_exit(&ksem->ks_lock); + + if (destroy) { + ksem_free(ksem); + } + if (fd != -1) { + fd_putfile(fd); + } +} + int sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, register_t *retval) @@ -409,18 +552,31 @@ sys__ksem_init(struct lwp *l, const stru intptr_t *idp; } */ - return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); + return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), + copyin, copyout); } int -do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout) +do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyin_t docopyin, + copyout_t docopyout) { proc_t *p = l->l_proc; ksem_t *ks; file_t *fp; - intptr_t id; + intptr_t id, arg; int fd, error; + /* + * Newer versions of librt / libpthread pass us 'PSRD' in *idp to + * indicate that a pshared semaphore is wanted. In that case we + * allocate globally unique ID and return that, rather than the + * process-scoped file descriptor ID. + */ + error = (*docopyin)(idp, &arg, sizeof(*idp)); + if (error) { + return error; + } + error = fd_allocfile(&fp, &fd); if (error) { return error; @@ -429,11 +585,14 @@ do_ksem_init(lwp_t *l, u_int val, intptr fp->f_flag = FREAD | FWRITE; fp->f_ops = &semops; - id = (intptr_t)fd; - error = (*docopyout)(&id, idp, sizeof(*idp)); - if (error) { + if (fd >= KSEM_MARKER_MIN) { + /* + * This is super-unlikely, but we check for it anyway + * because potential collisions with the pshared marker + * would be bad. + */ fd_abort(p, fp, fd); - return error; + return EMFILE; } /* Note the mode does not matter for anonymous semaphores. */ @@ -442,6 +601,23 @@ do_ksem_init(lwp_t *l, u_int val, intptr fd_abort(p, fp, fd); return error; } + + if (arg == KSEM_PSHARED) { + ks->ks_pshared_proc = curproc; + ks->ks_pshared_fd = fd; + ksem_alloc_pshared_id(ks); + id = ks->ks_pshared_id; + } else { + id = (intptr_t)fd; + } + + error = (*docopyout)(&id, idp, sizeof(*idp)); + if (error) { + ksem_free(ks); + fd_abort(p, fp, fd); + return error; + } + fp->f_ksem = ks; fd_affix(p, fp, fd); return error; @@ -487,6 +663,16 @@ do_ksem_open(struct lwp *l, const char * fp->f_flag = FREAD | FWRITE; fp->f_ops = &semops; + if (fd >= KSEM_MARKER_MIN) { + /* + * This is super-unlikely, but we check for it anyway + * because potential collisions with the pshared marker + * would be bad. + */ + fd_abort(p, fp, fd); + return EMFILE; + } + /* * The ID (file descriptor number) can be stored early. * Note that zero is a special value for libpthread. @@ -580,10 +766,24 @@ sys__ksem_close(struct lwp *l, const str /* { intptr_t id; } */ - int fd = (int)SCARG(uap, id); + intptr_t id = SCARG(uap, id); + int fd, error; + ksem_t *ks; + + error = ksem_get(id, &ks, &fd); + if (error) { + return error; + } - if (fd_getfile(fd) == NULL) { - return EBADF; + /* This is only for named semaphores. */ + if (ks->ks_name == NULL) { + error = EINVAL; + } + ksem_release(ks, -1); + if (error) { + if (fd != -1) + fd_putfile(fd); + return error; } return fd_close(fd); } @@ -639,22 +839,14 @@ static int ksem_close_fop(file_t *fp) { ksem_t *ks = fp->f_ksem; - bool destroy = false; - mutex_enter(&ks->ks_lock); - KASSERT(ks->ks_ref > 0); - if (--ks->ks_ref == 0) { - /* - * Destroy if the last reference and semaphore is unnamed, - * or unlinked (for named semaphore). - */ - destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL); + if (ks->ks_pshared_id != 0 && ks->ks_pshared_proc != curproc) { + /* Do nothing if this is not the creator. */ + return 0; } - mutex_exit(&ks->ks_lock); - if (destroy) { - ksem_free(ks); - } + mutex_enter(&ks->ks_lock); + ksem_release(ks, -1); return 0; } @@ -716,10 +908,10 @@ sys__ksem_post(struct lwp *l, const stru /* { intptr_t id; } */ - int fd = (int)SCARG(uap, id), error; + int fd, error; ksem_t *ks; - error = ksem_get(fd, &ks); + error = ksem_get(SCARG(uap, id), &ks, &fd); if (error) { return error; } @@ -733,18 +925,17 @@ sys__ksem_post(struct lwp *l, const stru cv_broadcast(&ks->ks_cv); } out: - mutex_exit(&ks->ks_lock); - fd_putfile(fd); + ksem_release(ks, fd); return error; } int do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime) { - int fd = (int)id, error, timeo; + int fd, error, timeo; ksem_t *ks; - error = ksem_get(fd, &ks); + error = ksem_get(id, &ks, &fd); if (error) { return error; } @@ -767,8 +958,7 @@ do_ksem_wait(lwp_t *l, intptr_t id, bool } ks->ks_value--; out: - mutex_exit(&ks->ks_lock); - fd_putfile(fd); + ksem_release(ks, fd); return error; } @@ -826,18 +1016,17 @@ sys__ksem_getvalue(struct lwp *l, const intptr_t id; unsigned int *value; } */ - int fd = (int)SCARG(uap, id), error; + int fd, error; ksem_t *ks; unsigned int val; - error = ksem_get(fd, &ks); + error = ksem_get(SCARG(uap, id), &ks, &fd); if (error) { return error; } KASSERT(mutex_owned(&ks->ks_lock)); val = ks->ks_value; - mutex_exit(&ks->ks_lock); - fd_putfile(fd); + ksem_release(ks, fd); return copyout(&val, SCARG(uap, value), sizeof(val)); } @@ -849,10 +1038,12 @@ sys__ksem_destroy(struct lwp *l, const s /* { intptr_t id; } */ - int fd = (int)SCARG(uap, id), error; + int fd, error; ksem_t *ks; - error = ksem_get(fd, &ks); + intptr_t id = SCARG(uap, id); + + error = ksem_get(id, &ks, &fd); if (error) { return error; } @@ -868,10 +1059,29 @@ sys__ksem_destroy(struct lwp *l, const s error = EBUSY; goto out; } + if (KSEM_ID_IS_PSHARED(id)) { + /* Cannot destroy if we did't create it. */ + KASSERT(fd == -1); + KASSERT(ks->ks_pshared_proc != NULL); + if (ks->ks_pshared_proc != curproc) { + error = EINVAL; + goto out; + } + fd = ks->ks_pshared_fd; + + /* Mark it dead so subsequent lookups fail. */ + ks->ks_pshared_proc = NULL; + + /* Do an fd_getfile() to for the benefit of fd_close(). */ + file_t *fp __diagused = fd_getfile(fd); + KASSERT(fp != NULL); + KASSERT(fp->f_ksem == ks); + } out: - mutex_exit(&ks->ks_lock); + ksem_release(ks, -1); if (error) { - fd_putfile(fd); + if (!KSEM_ID_IS_PSHARED(id)) + fd_putfile(fd); return error; } return fd_close(fd); Index: src/sys/sys/ksem.h diff -u src/sys/sys/ksem.h:1.14 src/sys/sys/ksem.h:1.15 --- src/sys/sys/ksem.h:1.14 Sun Nov 25 01:05:04 2012 +++ src/sys/sys/ksem.h Sun Feb 3 03:20:24 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: ksem.h,v 1.14 2012/11/25 01:05:04 christos Exp $ */ +/* $NetBSD: ksem.h,v 1.15 2019/02/03 03:20:24 thorpej Exp $ */ /* * Copyright (c) 2002 Alfred Perlstein <alf...@freebsd.org> @@ -38,8 +38,11 @@ struct timespec; typedef struct ksem { LIST_ENTRY(ksem) ks_entry; /* global list entry */ + struct proc * ks_pshared_proc;/* owner of pshared sem */ + intptr_t ks_pshared_id; /* global id for pshared sem */ kmutex_t ks_lock; /* lock on this ksem */ kcondvar_t ks_cv; /* condition variable */ + u_int ks_pshared_fd; /* fd in owning proc */ u_int ks_ref; /* number of references */ u_int ks_value; /* current value */ u_int ks_waiters; /* number of waiters */ @@ -51,13 +54,21 @@ typedef struct ksem { gid_t ks_gid; /* creator gid */ } ksem_t; -int do_ksem_init(struct lwp *, unsigned int, intptr_t *, copyout_t); +int do_ksem_init(struct lwp *, unsigned int, intptr_t *, copyin_t, copyout_t); int do_ksem_open(struct lwp *, const char *, int, mode_t, unsigned int, intptr_t *, copyout_t); int do_ksem_wait(struct lwp *, intptr_t, bool, struct timespec *); extern int ksem_max; -#endif +#endif /* _KERNEL */ + +#if defined(_KERNEL) || defined(_LIBC) +#define KSEM_PSHARED 0x50535244U /* 'PSRD' */ + +#define KSEM_MARKER_MASK 0xff000001U +#define KSEM_MARKER_MIN 0x01000001U +#define KSEM_PSHARED_MARKER 0x70000001U /* 'p' << 24 | 1 */ +#endif /* _KERNEL || _LIBC */ #ifdef _LIBC __BEGIN_DECLS Index: src/tests/kernel/Makefile diff -u src/tests/kernel/Makefile:1.56 src/tests/kernel/Makefile:1.57 --- src/tests/kernel/Makefile:1.56 Fri Jan 25 18:33:58 2019 +++ src/tests/kernel/Makefile Sun Feb 3 03:20:24 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.56 2019/01/25 18:33:58 christos Exp $ +# $NetBSD: Makefile,v 1.57 2019/02/03 03:20:24 thorpej Exp $ NOMAN= # defined @@ -14,6 +14,7 @@ TESTS_C+= t_mqueue TESTS_C+= t_sysv TESTS_C+= t_subr_prf TESTS_C+= t_kauth_pr_47598 +TESTS_C+= t_ksem TESTS_C+= t_sysctl TESTS_C+= t_timeleft TESTS_C+= t_zombie Index: src/tests/lib/librt/t_sem.c diff -u src/tests/lib/librt/t_sem.c:1.3 src/tests/lib/librt/t_sem.c:1.4 --- src/tests/lib/librt/t_sem.c:1.3 Sat Jan 14 20:58:20 2017 +++ src/tests/lib/librt/t_sem.c Sun Feb 3 03:20:24 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $ */ +/* $NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $ */ /* - * Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. + * Copyright (c) 2008, 2010, 2019 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,10 +56,11 @@ */ #include <sys/cdefs.h> -__COPYRIGHT("@(#) Copyright (c) 2008, 2010\ +__COPYRIGHT("@(#) Copyright (c) 2008, 2010, 2019\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $"); +__RCSID("$NetBSD: t_sem.c,v 1.4 2019/02/03 03:20:24 thorpej Exp $"); +#include <sys/mman.h> #include <sys/wait.h> #include <errno.h> @@ -173,11 +174,152 @@ ATF_TC_CLEANUP(child, tc) (void)sem_unlink("/sem_a"); } +ATF_TC_WITH_CLEANUP(pshared); +ATF_TC_HEAD(pshared, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks using pshared unnamed " + "semaphores to synchronize a master with multiple slave processes"); +} + +struct shared_region { + sem_t the_sem; +}; + +static struct shared_region * +get_shared_region(int o_flags) +{ + + int fd = shm_open("/shm_semtest_a", o_flags, 0644); + ATF_REQUIRE(fd != -1); + + ATF_REQUIRE_EQ(ftruncate(fd, sizeof(struct shared_region)), 0); + + void *rv = mmap(NULL, sizeof(struct shared_region), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE(rv != MAP_FAILED); + + (void)close(fd); + + return rv; +} + +static void +put_shared_region(struct shared_region *r) +{ + ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0); +} + +ATF_TC_BODY(pshared, tc) +{ + struct shared_region *master_region, *slave_region;; + + if (sysconf(_SC_SEMAPHORES) == -1) + atf_tc_skip("POSIX semaphores not supported"); + + /* + * Create a shared memory region to contain the pshared + * semaphore, create the semaphore there, and then detach + * from the shared memory region to ensure that our child + * processes will be getting at it from scratch. + */ + master_region = get_shared_region(O_RDWR | O_CREAT | O_EXCL); + ATF_REQUIRE(sem_init(&master_region->the_sem, 1, 0) == 0); + put_shared_region(master_region); + + /* + * Now execute a test that's essentially equivalent to the + * "child" test above, but using the pshared semaphore in the + * shared memory region. + */ + + pid_t pid, children[NCHILDREN]; + unsigned i, j; + int status; + + for (j = 1; j <= 2; j++) { + for (i = 0; i < NCHILDREN; i++) { + switch ((pid = fork())) { + case -1: + atf_tc_fail("fork() returned -1"); + case 0: + slave_region = get_shared_region(O_RDWR); + printf("PID %d waiting for semaphore...\n", + getpid()); + ATF_REQUIRE_MSG(sem_wait(&slave_region->the_sem) + == 0, + "sem_wait failed; iteration %d", j); + printf("PID %d got semaphore\n", getpid()); + _exit(0); + default: + children[i] = pid; + break; + } + } + + master_region = get_shared_region(O_RDWR); + + for (i = 0; i < NCHILDREN; i++) { + sleep(1); + printf("main loop %d: posting...\n", j); + ATF_REQUIRE_EQ(sem_post(&master_region->the_sem), 0); + } + + put_shared_region(master_region); + + for (i = 0; i < NCHILDREN; i++) { + ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + } + } + + master_region = get_shared_region(O_RDWR); + ATF_REQUIRE_EQ(sem_destroy(&master_region->the_sem), 0); + put_shared_region(master_region); + + ATF_REQUIRE_EQ(shm_unlink("/shm_semtest_a"), 0); +} +ATF_TC_CLEANUP(pshared, tc) +{ + /* + * The kernel will g/c the pshared semaphore when the process that + * created it exits, so no need to include that in the cleanup here. + */ + (void)shm_unlink("/shm_semtest_a"); +} + +ATF_TC_WITH_CLEANUP(invalid_ops); +ATF_TC_HEAD(invalid_ops, tc) +{ + atf_tc_set_md_var(tc, "descr", "Validates behavior when calling " + "bad operations for the semaphore type"); +} +ATF_TC_BODY(invalid_ops, tc) +{ + sem_t *sem; + sem_t the_sem; + + sem = sem_open("/sem_c", O_CREAT | O_EXCL, 0644, 0); + ATF_REQUIRE(sem != SEM_FAILED); + ATF_REQUIRE(sem_destroy(sem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(sem_close(sem), 0); + + ATF_REQUIRE_EQ(sem_init(&the_sem, 0, 0), 0); + ATF_REQUIRE(sem_close(&the_sem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(sem_destroy(&the_sem), 0); +} +ATF_TC_CLEANUP(invalid_ops, tc) +{ + (void)sem_unlink("/sem_c"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, basic); ATF_TP_ADD_TC(tp, child); + ATF_TP_ADD_TC(tp, pshared); + ATF_TP_ADD_TC(tp, invalid_ops); return atf_no_error(); } Index: src/usr.bin/fstat/misc.c diff -u src/usr.bin/fstat/misc.c:1.20 src/usr.bin/fstat/misc.c:1.21 --- src/usr.bin/fstat/misc.c:1.20 Tue Jun 26 10:00:25 2018 +++ src/usr.bin/fstat/misc.c Sun Feb 3 03:20:24 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: misc.c,v 1.20 2018/06/26 10:00:25 msaitoh Exp $ */ +/* $NetBSD: misc.c,v 1.21 2019/02/03 03:20:24 thorpej Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: misc.c,v 1.20 2018/06/26 10:00:25 msaitoh Exp $"); +__RCSID("$NetBSD: misc.c,v 1.21 2019/02/03 03:20:24 thorpej Exp $"); #include <stdbool.h> #include <sys/param.h> @@ -47,6 +47,7 @@ __RCSID("$NetBSD: misc.c,v 1.20 2018/06/ #include <sys/proc.h> #define _KERNEL #include <sys/file.h> +#define copyin_t int #define copyout_t int #include <sys/ksem.h> #define _LIB_LIBKERN_LIBKERN_H_ Added files: Index: src/tests/kernel/t_ksem.c diff -u /dev/null src/tests/kernel/t_ksem.c:1.1 --- /dev/null Sun Feb 3 03:20:24 2019 +++ src/tests/kernel/t_ksem.c Sun Feb 3 03:20:24 2019 @@ -0,0 +1,145 @@ +/* $NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $"); + +#include <sys/mman.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +#include <atf-c.h> + +#define _LIBC +#include <sys/ksem.h> + +ATF_TC(close_on_unnamed); +ATF_TC_HEAD(close_on_unnamed, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on close of unnamed semaphore"); +} +ATF_TC_BODY(close_on_unnamed, tc) +{ + intptr_t ksem; + + /* _ksem_close() is invalid on unnamed semaphore. */ + ksem = 0; + ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0); + ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0); +} + +ATF_TC(close_on_unnamed_pshared); +ATF_TC_HEAD(close_on_unnamed_pshared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on close of unnamed pshared semaphore"); +} +ATF_TC_BODY(close_on_unnamed_pshared, tc) +{ + intptr_t ksem; + + /* Similar, but lifecycle of pshared is slightly different. */ + ksem = KSEM_PSHARED; + ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0); + ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0); +} + +ATF_TC_WITH_CLEANUP(destroy_on_named); +ATF_TC_HEAD(destroy_on_named, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on destroy of named semaphore"); +} +ATF_TC_BODY(destroy_on_named, tc) +{ + intptr_t ksem; + + /* Exercise open-unlinked semaphore lifecycle. */ + ATF_REQUIRE_EQ(_ksem_open("/ksem_x", O_CREAT | O_EXCL, 0644, 0, &ksem), + 0); + ATF_REQUIRE(_ksem_destroy(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_close(ksem), 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_x"), 0); +} +ATF_TC_CLEANUP(destroy_on_named, tc) +{ + (void)_ksem_unlink("/ksem_x"); +} + +ATF_TC_WITH_CLEANUP(open_unlinked_lifecycle); +ATF_TC_HEAD(open_unlinked_lifecycle, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Exercises lifecycle of open-unlined semaphores"); +} +ATF_TC_BODY(open_unlinked_lifecycle, tc) +{ + intptr_t ksem, ksem1; + int val; + + /* Exercise open-unlinked semaphore lifecycle. */ + ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 0, &ksem), + 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0); + val = 255; + ATF_REQUIRE(_ksem_getvalue(ksem, &val) == 0 && val == 0); + + ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 1, &ksem1), + 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0); + val = 255; + ATF_REQUIRE(_ksem_getvalue(ksem1, &val) == 0 && val == 1); + + ATF_REQUIRE_EQ(_ksem_close(ksem), 0); + ATF_REQUIRE_EQ(_ksem_close(ksem1), 0); +} +ATF_TC_CLEANUP(open_unlinked_lifecycle, tc) +{ + (void)_ksem_unlink("/ksem_a"); + (void)_ksem_unlink("/ksem_b"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, close_on_unnamed); + ATF_TP_ADD_TC(tp, close_on_unnamed_pshared); + ATF_TP_ADD_TC(tp, destroy_on_named); + ATF_TP_ADD_TC(tp, open_unlinked_lifecycle); + + return atf_no_error(); +}