diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index ca32a0fdaa8f5..985e5780c0962 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -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 diff --git a/lib/libpthread/Makefile b/lib/libpthread/Makefile index 3f06019ff2115..82ccce8fad9a0 100644 --- a/lib/libpthread/Makefile +++ b/lib/libpthread/Makefile @@ -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) diff --git a/lib/libpthread/sem.c b/lib/libpthread/sem.c deleted file mode 100644 index 5c3e7fbb68a5a..0000000000000 --- a/lib/libpthread/sem.c +++ /dev/null @@ -1,316 +0,0 @@ -/* $NetBSD: sem.c,v 1.24 2012/03/10 18:01:10 joerg Exp $ */ - -/*- - * Copyright (c) 2003, 2006, 2007 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Jason R. Thorpe of Wasabi Systems, Inc, and by Andrew Doran. - * - * 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. - */ - -/* - * Copyright (C) 2000 Jason Evans . - * 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(s), this list of conditions and the following disclaimer as - * the first lines of this file unmodified other than the possible - * addition of one or more copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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 -__RCSID("$NetBSD: sem.c,v 1.24 2012/03/10 18:01:10 joerg Exp $"); - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pthread.h" - -struct _sem_st { - unsigned int ksem_magic; -#define KSEM_MAGIC 0x90af0421U - - 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); -static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; - -static void -sem_free(sem_t sem) -{ - - sem->ksem_magic = 0; - free(sem); -} - -static int -sem_alloc(unsigned int value, intptr_t semid, sem_t *semp) -{ - sem_t sem; - - if (value > SEM_VALUE_MAX) - return (EINVAL); - - if ((sem = malloc(sizeof(struct _sem_st))) == NULL) - return (ENOSPC); - - sem->ksem_magic = KSEM_MAGIC; - sem->ksem_semid = semid; - - *semp = sem; - return (0); -} - -/* ARGSUSED */ -int -sem_init(sem_t *sem, int pshared, unsigned int value) -{ - intptr_t semid; - int error; - - if (_ksem_init(value, &semid) == -1) - return (-1); - - if ((error = sem_alloc(value, semid, sem)) != 0) { - _ksem_destroy(semid); - errno = error; - return (-1); - } - - return (0); -} - -int -sem_destroy(sem_t *sem) -{ - int error, save_errno; - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - error = _ksem_destroy((*sem)->ksem_semid); - save_errno = errno; - sem_free(*sem); - errno = save_errno; - - return error; -} - -sem_t * -sem_open(const char *name, int oflag, ...) -{ - sem_t *sem, s; - intptr_t semid; - mode_t mode; - unsigned int value; - int error; - va_list ap; - - mode = 0; - value = 0; - - if (oflag & O_CREAT) { - va_start(ap, oflag); - mode = va_arg(ap, int); - value = va_arg(ap, unsigned int); - va_end(ap); - } - - /* - * We can be lazy and let the kernel handle the oflag, - * we'll just merge duplicate IDs into our list. - */ - if (_ksem_open(name, oflag, mode, value, &semid) == -1) - return (SEM_FAILED); - - /* - * Search for a duplicate ID, we must return the same sem_t * - * if we locate one. - */ - pthread_mutex_lock(&named_sems_mtx); - LIST_FOREACH(s, &named_sems, ksem_list) { - if (s->ksem_semid == semid) { - pthread_mutex_unlock(&named_sems_mtx); - return (s->ksem_identity); - } - } - - if ((sem = malloc(sizeof(*sem))) == NULL) { - error = ENOSPC; - goto bad; - } - if ((error = sem_alloc(value, semid, sem)) != 0) - goto bad; - - LIST_INSERT_HEAD(&named_sems, *sem, ksem_list); - pthread_mutex_unlock(&named_sems_mtx); - (*sem)->ksem_identity = sem; - - return (sem); - - bad: - pthread_mutex_unlock(&named_sems_mtx); - _ksem_close(semid); - if (sem != NULL) { - if (*sem != NULL) - sem_free(*sem); - free(sem); - } - errno = error; - return (SEM_FAILED); -} - -int -sem_close(sem_t *sem) -{ - int error, save_errno; - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - pthread_mutex_lock(&named_sems_mtx); - error = _ksem_close((*sem)->ksem_semid); - LIST_REMOVE((*sem), ksem_list); - save_errno = errno; - pthread_mutex_unlock(&named_sems_mtx); - sem_free(*sem); - free(sem); - errno = save_errno; - return error; -} - -int -sem_unlink(const char *name) -{ - - return (_ksem_unlink(name)); -} - -int -sem_wait(sem_t *sem) -{ - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - return (_ksem_wait((*sem)->ksem_semid)); -} - -int -sem_timedwait(sem_t *sem, const struct timespec * __restrict abstime) -{ - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - return (_ksem_timedwait((*sem)->ksem_semid, abstime)); -} - -int -sem_trywait(sem_t *sem) -{ - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - return (_ksem_trywait((*sem)->ksem_semid)); -} - -int -sem_post(sem_t *sem) -{ - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - - return (_ksem_post((*sem)->ksem_semid)); -} - -int -sem_getvalue(sem_t * __restrict sem, int * __restrict sval) -{ - -#ifdef ERRORCHECK - if (sem == NULL || *sem == NULL || (*sem)->ksem_magic != KSEM_MAGIC) { - errno = EINVAL; - return (-1); - } -#endif - return (_ksem_getvalue((*sem)->ksem_semid, sval)); -} diff --git a/lib/librt/sem.c b/lib/librt/sem.c index 18f8b192efdfb..df9cd048506ad 100644 --- a/lib/librt/sem.c +++ b/lib/librt/sem.c @@ -1,7 +1,7 @@ /* $NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg 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 @@ -61,10 +61,16 @@ #include __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg 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 19:59:21 joerg Exp $"); #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 @@ -88,20 +96,47 @@ __RCSID("$NetBSD: sem.c,v 1.7 2012/03/10 19:59:21 joerg Exp $"); #include #include +#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_timedwait) __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 semid, sem_t *semp) 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 semid, sem_t *semp) 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 timespec * __restrict abstime) { #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 * __restrict sval) { #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)); } diff --git a/sys/compat/netbsd32/netbsd32_sem.c b/sys/compat/netbsd32/netbsd32_sem.c index 31546599a9112..b3740febbea9f 100644 --- a/sys/compat/netbsd32/netbsd32_sem.c +++ b/sys/compat/netbsd32/netbsd32_sem.c @@ -44,6 +44,21 @@ __KERNEL_RCSID(0, "$NetBSD: netbsd32_sem.c,v 1.11 2014/09/19 17:25:33 matt Exp $ #include #include +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) { @@ -53,6 +68,7 @@ netbsd32_ksem_copyout(const void *src, void *dst, size_t size) 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 struct netbsd32__ksem_init_args *uap, r } */ 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 diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c index b8d23cbca56da..a4b776ca68d54 100644 --- a/sys/kern/uipc_sem.c +++ b/sys/kern/uipc_sem.c @@ -1,11 +1,11 @@ /* $NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos 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 @@ -67,6 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $ #include #include +#include #include #include #include @@ -77,11 +78,14 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $ #include #include #include +#include +#include #include #include #include #include #include +#include MODULE(MODULE_CLASS_MISC, ksem, NULL); @@ -95,6 +99,12 @@ static LIST_HEAD(,ksem) ksem_head __cacheline_aligned; 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; @@ -295,29 +312,122 @@ ksem_perm(lwp_t *l, ksem_t *ks) return 0; } +/* + * 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 struct sys__ksem_init_args *uap, 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_t *idp, copyout_t docopyout) 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_t *idp, copyout_t docopyout) 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 *semname, int oflag, mode_t mode, 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 struct sys__ksem_close_args *uap, /* { intptr_t id; } */ - int fd = (int)SCARG(uap, id); + intptr_t id = SCARG(uap, id); + int fd, error; + ksem_t *ks; - if (fd_getfile(fd) == NULL) { - return EBADF; + error = ksem_get(id, &ks, &fd); + if (error) { + return error; + } + + /* 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 struct sys__ksem_post_args *uap, /* { 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 struct sys__ksem_post_args *uap, 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 try_p, struct timespec *abstime) } 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 struct sys__ksem_getvalue_args *uap, 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 struct sys__ksem_destroy_args *uap, /* { 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 struct sys__ksem_destroy_args *uap, 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); diff --git a/sys/sys/ksem.h b/sys/sys/ksem.h index 6ec33782a6b07..380933b4a36da 100644 --- a/sys/sys/ksem.h +++ b/sys/sys/ksem.h @@ -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 diff --git a/tests/kernel/Makefile b/tests/kernel/Makefile index 38c91becedc82..f566ccfa256f0 100644 --- a/tests/kernel/Makefile +++ b/tests/kernel/Makefile @@ -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 diff --git a/tests/kernel/t_ksem.c b/tests/kernel/t_ksem.c new file mode 100644 index 0000000000000..c67636de14ed6 --- /dev/null +++ b/tests/kernel/t_ksem.c @@ -0,0 +1,145 @@ +/* $NetBSD$ */ + +/* + * 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD$"); + +#include +#include + +#include +#include +#include +#include + +#include + +#define _LIBC +#include + +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(); +} diff --git a/tests/lib/librt/t_sem.c b/tests/lib/librt/t_sem.c index 0541ae57113ac..0164b437d9273 100644 --- a/tests/lib/librt/t_sem.c +++ b/tests/lib/librt/t_sem.c @@ -1,7 +1,7 @@ /* $NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos 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 -__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 $"); +#include #include #include @@ -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(); } diff --git a/usr.bin/fstat/misc.c b/usr.bin/fstat/misc.c index ceaa2a9b638e0..f2152d7b0ee8c 100644 --- a/usr.bin/fstat/misc.c +++ b/usr.bin/fstat/misc.c @@ -47,6 +47,7 @@ __RCSID("$NetBSD: misc.c,v 1.20 2018/06/26 10:00:25 msaitoh Exp $"); #include #define _KERNEL #include +#define copyin_t int #define copyout_t int #include #define _LIB_LIBKERN_LIBKERN_H_