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();
+}

Reply via email to