summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_descrip.c6
-rw-r--r--sys/kern/uipc_sem.c1186
-rw-r--r--sys/kern/uipc_shm.c4
-rw-r--r--sys/modules/sem/Makefile2
-rw-r--r--sys/security/mac/mac_framework.h11
-rw-r--r--sys/security/mac/mac_policy.h19
-rw-r--r--sys/security/mac/mac_posix_sem.c30
-rw-r--r--sys/security/mac_biba/mac_biba.c31
-rw-r--r--sys/security/mac_mls/mac_mls.c31
-rw-r--r--sys/security/mac_stub/mac_stub.c21
-rw-r--r--sys/security/mac_test/mac_test.c35
-rw-r--r--sys/sys/file.h1
-rw-r--r--sys/sys/ksem.h42
-rw-r--r--sys/sys/user.h1
-rw-r--r--tools/regression/posixsem/Makefile11
-rw-r--r--tools/regression/posixsem/posixsem.c1437
-rw-r--r--tools/regression/posixsem/posixsem.t5
-rw-r--r--tools/regression/posixsem/test.c128
-rw-r--r--tools/regression/posixsem/test.h59
-rw-r--r--usr.bin/procstat/procstat_files.c4
20 files changed, 2379 insertions, 685 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 0bd3ef6..f4e5f56 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -2633,6 +2633,10 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS)
kif->kf_type = KF_TYPE_SHM;
break;
+ case DTYPE_SEM:
+ kif->kf_type = KF_TYPE_SEM;
+ break;
+
default:
kif->kf_type = KF_TYPE_UNKNOWN;
break;
@@ -2767,6 +2771,8 @@ file_type_to_name(short type)
return ("mque");
case DTYPE_SHM:
return ("shm");
+ case DTYPE_SEM:
+ return ("ksem");
default:
return ("unkn");
}
diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c
index 9603b1d..47b711a 100644
--- a/sys/kern/uipc_sem.c
+++ b/sys/kern/uipc_sem.c
@@ -38,585 +38,595 @@ __FBSDID("$FreeBSD$");
#include "opt_posix.h"
#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/sysproto.h>
-#include <sys/eventhandler.h>
+#include <sys/condvar.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/fnv_hash.h>
#include <sys/kernel.h>
#include <sys/ksem.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/posix4.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/module.h>
-#include <sys/condvar.h>
-#include <sys/sem.h>
-#include <sys/uio.h>
#include <sys/semaphore.h>
-#include <sys/syscall.h>
+#include <sys/_semaphore.h>
#include <sys/stat.h>
-#include <sys/sysent.h>
+#include <sys/syscall.h>
+#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
-#include <sys/time.h>
-#include <sys/malloc.h>
-#include <sys/fcntl.h>
-#include <sys/_semaphore.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/systm.h>
+#include <sys/sx.h>
+#include <sys/vnode.h>
#include <security/mac/mac_framework.h>
-static int sem_count_proc(struct proc *p);
-static struct ksem *sem_lookup_byname(const char *name);
-static int sem_create(struct thread *td, const char *name,
- struct ksem **ksret, mode_t mode, unsigned int value);
-static void sem_free(struct ksem *ksnew);
-static int sem_perm(struct thread *td, struct ksem *ks);
-static void sem_enter(struct proc *p, struct ksem *ks);
-static int sem_leave(struct proc *p, struct ksem *ks);
-static void sem_exechook(void *arg, struct proc *p,
- struct image_params *imgp);
-static void sem_exithook(void *arg, struct proc *p);
-static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
- int flags);
-static int sem_hasopen(struct thread *td, struct ksem *ks);
-
-static int kern_sem_close(struct thread *td, semid_t id);
-static int kern_sem_post(struct thread *td, semid_t id);
-static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
- struct timespec *abstime);
-static int kern_sem_init(struct thread *td, int dir, unsigned int value,
- semid_t *idp);
-static int kern_sem_open(struct thread *td, int dir, const char *name,
- int oflag, mode_t mode, unsigned int value, semid_t *idp);
-static int kern_sem_unlink(struct thread *td, const char *name);
+/*
+ * TODO
+ *
+ * - Resource limits?
+ * - Update fstat(1)
+ * - Replace global sem_lock with mtx_pool locks?
+ * - Add a MAC check_create() hook for creating new named semaphores.
+ */
#ifndef SEM_MAX
#define SEM_MAX 30
#endif
-#define SEM_MAX_NAMELEN 14
-
-#define SEM_TO_ID(x) ((intptr_t)(x))
-#define ID_TO_SEM(x) id_to_sem(x)
-
-/*
- * Available semaphores go here, this includes sem_init and any semaphores
- * created via sem_open that have not yet been unlinked.
- */
-LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
+#ifdef SEM_DEBUG
+#define DP(x) printf x
+#else
+#define DP(x)
+#endif
-/*
- * Semaphores still in use but have been sem_unlink()'d go here.
- */
-LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
+struct ksem_mapping {
+ char *km_path;
+ Fnv32_t km_fnv;
+ struct ksem *km_ksem;
+ LIST_ENTRY(ksem_mapping) km_link;
+};
+static MALLOC_DEFINE(M_KSEM, "ksem", "semaphore file descriptor");
+static LIST_HEAD(, ksem_mapping) *ksem_dictionary;
+static struct sx ksem_dict_lock;
+static struct mtx ksem_count_lock;
static struct mtx sem_lock;
-static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
+static u_long ksem_hash;
+static int ksem_dead;
+
+#define KSEM_HASH(fnv) (&ksem_dictionary[(fnv) & ksem_hash])
static int nsems = 0;
SYSCTL_DECL(_p1003_1b);
-SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
+SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0,
+ "Number of active kernel POSIX semaphores");
+
+static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
+ struct timespec *abstime);
+static int ksem_access(struct ksem *ks, struct ucred *ucred);
+static struct ksem *ksem_alloc(struct ucred *ucred, mode_t mode,
+ unsigned int value);
+static int ksem_create(struct thread *td, const char *path,
+ semid_t *semidp, mode_t mode, unsigned int value,
+ int flags);
+static void ksem_drop(struct ksem *ks);
+static int ksem_get(struct thread *td, semid_t id, struct file **fpp);
+static struct ksem *ksem_hold(struct ksem *ks);
+static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks);
+static struct ksem *ksem_lookup(char *path, Fnv32_t fnv);
+static void ksem_module_destroy(void);
+static int ksem_module_init(void);
+static int ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred);
+static int sem_modload(struct module *module, int cmd, void *arg);
+
+static fo_rdwr_t ksem_read;
+static fo_rdwr_t ksem_write;
+static fo_truncate_t ksem_truncate;
+static fo_ioctl_t ksem_ioctl;
+static fo_poll_t ksem_poll;
+static fo_kqfilter_t ksem_kqfilter;
+static fo_stat_t ksem_stat;
+static fo_close_t ksem_closef;
+
+/* File descriptor operations. */
+static struct fileops ksem_ops = {
+ .fo_read = ksem_read,
+ .fo_write = ksem_write,
+ .fo_truncate = ksem_truncate,
+ .fo_ioctl = ksem_ioctl,
+ .fo_poll = ksem_poll,
+ .fo_kqfilter = ksem_kqfilter,
+ .fo_stat = ksem_stat,
+ .fo_close = ksem_closef,
+ .fo_flags = DFLAG_PASSABLE
+};
-static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
+FEATURE(posix_sem, "POSIX semaphores");
-#ifdef SEM_DEBUG
-#define DP(x) printf x
-#else
-#define DP(x)
-#endif
+static int
+ksem_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
+ int flags, struct thread *td)
+{
+
+ return (EOPNOTSUPP);
+}
-static __inline void
-sem_ref(struct ksem *ks)
+static int
+ksem_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
+ int flags, struct thread *td)
{
- mtx_assert(&sem_lock, MA_OWNED);
- ks->ks_ref++;
- DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
+ return (EOPNOTSUPP);
}
-static __inline void
-sem_rel(struct ksem *ks)
+static int
+ksem_truncate(struct file *fp, off_t length, struct ucred *active_cred,
+ struct thread *td)
{
- mtx_assert(&sem_lock, MA_OWNED);
- DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
- if (--ks->ks_ref == 0)
- sem_free(ks);
+ return (EINVAL);
}
-static __inline
-struct ksem *
-id_to_sem(semid_t id)
+static int
+ksem_ioctl(struct file *fp, u_long com, void *data,
+ struct ucred *active_cred, struct thread *td)
{
- struct ksem *ks;
- mtx_assert(&sem_lock, MA_OWNED);
- DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
- LIST_FOREACH(ks, &ksem_head, ks_entry) {
- DP(("id_to_sem: ks = %p\n", ks));
- if (ks == (struct ksem *)id)
- return (ks);
- }
- return (NULL);
+ return (EOPNOTSUPP);
}
-static struct ksem *
-sem_lookup_byname(const char *name)
+static int
+ksem_poll(struct file *fp, int events, struct ucred *active_cred,
+ struct thread *td)
{
- struct ksem *ks;
- mtx_assert(&sem_lock, MA_OWNED);
- LIST_FOREACH(ks, &ksem_head, ks_entry)
- if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
- return (ks);
- return (NULL);
+ return (EOPNOTSUPP);
}
static int
-sem_create(struct thread *td, const char *name, struct ksem **ksret,
- mode_t mode, unsigned int value)
+ksem_kqfilter(struct file *fp, struct knote *kn)
{
- struct ksem *ret;
- struct proc *p;
- struct ucred *uc;
- size_t len;
+
+ return (EOPNOTSUPP);
+}
+
+static int
+ksem_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct ksem *ks;
+#ifdef MAC
int error;
+#endif
- DP(("sem_create\n"));
- p = td->td_proc;
- uc = td->td_ucred;
- if (value > SEM_VALUE_MAX)
- return (EINVAL);
- ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
- if (name != NULL) {
- len = strlen(name);
- if (len > SEM_MAX_NAMELEN) {
- free(ret, M_SEM);
- return (ENAMETOOLONG);
- }
+ ks = fp->f_data;
- /* Name must start with a '/' but not contain one. */
- if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
- free(ret, M_SEM);
- return (EINVAL);
- }
- ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
- strcpy(ret->ks_name, name);
- } else {
- ret->ks_name = NULL;
- }
- ret->ks_mode = mode;
- ret->ks_value = value;
- ret->ks_ref = 1;
- ret->ks_waiters = 0;
- ret->ks_uid = uc->cr_uid;
- ret->ks_gid = uc->cr_gid;
- ret->ks_onlist = 0;
- cv_init(&ret->ks_cv, "sem");
- LIST_INIT(&ret->ks_users);
#ifdef MAC
- mac_posixsem_init(ret);
- mac_posixsem_create(uc, ret);
+ error = mac_posixsem_check_stat(active_cred, fp->f_cred, ks);
+ if (error)
+ return (error);
#endif
- if (name != NULL)
- sem_enter(td->td_proc, ret);
- *ksret = ret;
- mtx_lock(&sem_lock);
- nsems++;
- if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
- sem_leave(td->td_proc, ret);
- sem_free(ret);
- error = ENFILE;
- } else
- error = 0;
- mtx_unlock(&sem_lock);
- return (error);
+
+ /*
+ * Attempt to return sanish values for fstat() on a semaphore
+ * file descriptor.
+ */
+ bzero(sb, sizeof(*sb));
+ sb->st_mode = S_IFREG | ks->ks_mode; /* XXX */
+
+ sb->st_atimespec = ks->ks_atime;
+ sb->st_ctimespec = ks->ks_ctime;
+ sb->st_mtimespec = ks->ks_mtime;
+ sb->st_birthtimespec = ks->ks_birthtime;
+ sb->st_uid = ks->ks_uid;
+ sb->st_gid = ks->ks_gid;
+
+ return (0);
}
-#ifndef _SYS_SYSPROTO_H_
-struct ksem_init_args {
- unsigned int value;
- semid_t *idp;
-};
-int ksem_init(struct thread *td, struct ksem_init_args *uap);
-#endif
-int
-ksem_init(struct thread *td, struct ksem_init_args *uap)
+static int
+ksem_closef(struct file *fp, struct thread *td)
{
+ struct ksem *ks;
+
+ ks = fp->f_data;
+ fp->f_data = NULL;
+ ksem_drop(ks);
- return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
+ return (0);
}
-static int
-kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
+/*
+ * ksem object management including creation and reference counting
+ * routines.
+ */
+static struct ksem *
+ksem_alloc(struct ucred *ucred, mode_t mode, unsigned int value)
{
struct ksem *ks;
- semid_t id;
- int error;
- error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
- if (error)
- return (error);
- id = SEM_TO_ID(ks);
- if (dir == UIO_USERSPACE) {
- error = copyout(&id, idp, sizeof(id));
- if (error) {
- mtx_lock(&sem_lock);
- sem_rel(ks);
- mtx_unlock(&sem_lock);
- return (error);
- }
- } else {
- *idp = id;
+ mtx_lock(&ksem_count_lock);
+ if (nsems == p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX) || ksem_dead) {
+ mtx_unlock(&ksem_count_lock);
+ return (NULL);
}
- mtx_lock(&sem_lock);
- LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
- ks->ks_onlist = 1;
- mtx_unlock(&sem_lock);
- return (error);
+ nsems++;
+ mtx_unlock(&ksem_count_lock);
+ ks = malloc(sizeof(*ks), M_KSEM, M_WAITOK | M_ZERO);
+ ks->ks_uid = ucred->cr_uid;
+ ks->ks_gid = ucred->cr_gid;
+ ks->ks_mode = mode;
+ ks->ks_value = value;
+ cv_init(&ks->ks_cv, "ksem");
+ vfs_timestamp(&ks->ks_birthtime);
+ ks->ks_atime = ks->ks_mtime = ks->ks_ctime = ks->ks_birthtime;
+ refcount_init(&ks->ks_ref, 1);
+#ifdef MAC
+ mac_posixsem_init(ks);
+ mac_posixsem_create(ucred, ks);
+#endif
+
+ return (ks);
}
-#ifndef _SYS_SYSPROTO_H_
-struct ksem_open_args {
- char *name;
- int oflag;
- mode_t mode;
- unsigned int value;
- semid_t *idp;
-};
-int ksem_open(struct thread *td, struct ksem_open_args *uap);
+static struct ksem *
+ksem_hold(struct ksem *ks)
+{
+
+ refcount_acquire(&ks->ks_ref);
+ return (ks);
+}
+
+static void
+ksem_drop(struct ksem *ks)
+{
+
+ if (refcount_release(&ks->ks_ref)) {
+#ifdef MAC
+ mac_posixsem_destroy(ks);
#endif
-int
-ksem_open(struct thread *td, struct ksem_open_args *uap)
+ cv_destroy(&ks->ks_cv);
+ free(ks, M_KSEM);
+ mtx_lock(&ksem_count_lock);
+ nsems--;
+ mtx_unlock(&ksem_count_lock);
+ }
+}
+
+/*
+ * Determine if the credentials have sufficient permissions for read
+ * and write access.
+ */
+static int
+ksem_access(struct ksem *ks, struct ucred *ucred)
{
- char name[SEM_MAX_NAMELEN + 1];
- size_t done;
int error;
- error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
+ error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid,
+ VREAD | VWRITE, ucred, NULL);
if (error)
- return (error);
- DP((">>> sem_open start\n"));
- error = kern_sem_open(td, UIO_USERSPACE,
- name, uap->oflag, uap->mode, uap->value, uap->idp);
- DP(("<<< sem_open end\n"));
+ error = priv_check_cred(ucred, PRIV_SEM_WRITE, 0);
return (error);
}
-static int
-kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
- mode_t mode, unsigned int value, semid_t *idp)
+/*
+ * Dictionary management. We maintain an in-kernel dictionary to map
+ * paths to semaphore objects. We use the FNV hash on the path to
+ * store the mappings in a hash table.
+ */
+static struct ksem *
+ksem_lookup(char *path, Fnv32_t fnv)
{
- struct ksem *ksnew, *ks;
- int error;
- semid_t id;
+ struct ksem_mapping *map;
- ksnew = NULL;
- mtx_lock(&sem_lock);
- ks = sem_lookup_byname(name);
-
- /*
- * If we found it but O_EXCL is set, error.
- */
- if (ks != NULL && (oflag & O_EXCL) != 0) {
- mtx_unlock(&sem_lock);
- return (EEXIST);
+ LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
+ if (map->km_fnv != fnv)
+ continue;
+ if (strcmp(map->km_path, path) == 0)
+ return (map->km_ksem);
}
- /*
- * If we didn't find it...
- */
- if (ks == NULL) {
- /*
- * didn't ask for creation? error.
- */
- if ((oflag & O_CREAT) == 0) {
- mtx_unlock(&sem_lock);
- return (ENOENT);
- }
+ return (NULL);
+}
- /*
- * We may block during creation, so drop the lock.
- */
- mtx_unlock(&sem_lock);
- error = sem_create(td, name, &ksnew, mode, value);
- if (error != 0)
- return (error);
- id = SEM_TO_ID(ksnew);
- if (dir == UIO_USERSPACE) {
- DP(("about to copyout! %d to %p\n", id, idp));
- error = copyout(&id, idp, sizeof(id));
- if (error) {
- mtx_lock(&sem_lock);
- sem_leave(td->td_proc, ksnew);
- sem_rel(ksnew);
- mtx_unlock(&sem_lock);
- return (error);
- }
- } else {
- DP(("about to set! %d to %p\n", id, idp));
- *idp = id;
- }
+static void
+ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks)
+{
+ struct ksem_mapping *map;
- /*
- * We need to make sure we haven't lost a race while
- * allocating during creation.
- */
- mtx_lock(&sem_lock);
- ks = sem_lookup_byname(name);
- if (ks != NULL) {
- /* we lost... */
- sem_leave(td->td_proc, ksnew);
- sem_rel(ksnew);
- /* we lost and we can't loose... */
- if ((oflag & O_EXCL) != 0) {
- mtx_unlock(&sem_lock);
- return (EEXIST);
- }
- } else {
- DP(("sem_create: about to add to list...\n"));
- LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
- DP(("sem_create: setting list bit...\n"));
- ksnew->ks_onlist = 1;
- DP(("sem_create: done, about to unlock...\n"));
- }
- } else {
+ map = malloc(sizeof(struct ksem_mapping), M_KSEM, M_WAITOK);
+ map->km_path = path;
+ map->km_fnv = fnv;
+ map->km_ksem = ksem_hold(ks);
+ LIST_INSERT_HEAD(KSEM_HASH(fnv), map, km_link);
+}
+
+static int
+ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred)
+{
+ struct ksem_mapping *map;
+ int error;
+
+ LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
+ if (map->km_fnv != fnv)
+ continue;
+ if (strcmp(map->km_path, path) == 0) {
#ifdef MAC
- error = mac_posixsem_check_open(td->td_ucred, ks);
- if (error)
- goto err_open;
+ error = mac_posixsem_check_unlink(ucred, map->km_ksem);
+ if (error)
+ return (error);
#endif
- /*
- * if we aren't the creator, then enforce permissions.
- */
- error = sem_perm(td, ks);
- if (error)
- goto err_open;
- sem_ref(ks);
- mtx_unlock(&sem_lock);
- id = SEM_TO_ID(ks);
- if (dir == UIO_USERSPACE) {
- error = copyout(&id, idp, sizeof(id));
- if (error) {
- mtx_lock(&sem_lock);
- sem_rel(ks);
- mtx_unlock(&sem_lock);
+ error = ksem_access(map->km_ksem, ucred);
+ if (error)
return (error);
- }
- } else {
- *idp = id;
+ LIST_REMOVE(map, km_link);
+ ksem_drop(map->km_ksem);
+ free(map->km_path, M_KSEM);
+ free(map, M_KSEM);
+ return (0);
}
- sem_enter(td->td_proc, ks);
- mtx_lock(&sem_lock);
- sem_rel(ks);
}
-err_open:
- mtx_unlock(&sem_lock);
- return (error);
+
+ return (ENOENT);
}
+/* Other helper routines. */
static int
-sem_perm(struct thread *td, struct ksem *ks)
+ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode,
+ unsigned int value, int flags)
{
- struct ucred *uc;
+ struct filedesc *fdp;
+ struct ksem *ks;
+ struct file *fp;
+ char *path;
+ semid_t semid;
+ Fnv32_t fnv;
+ int error, fd;
+
+ if (value > SEM_VALUE_MAX)
+ return (EINVAL);
+
+ fdp = td->td_proc->p_fd;
+ mode = (mode & ~fdp->fd_cmask) & ACCESSPERMS;
+ error = falloc(td, &fp, &fd);
+ if (error) {
+ if (name == NULL)
+ error = ENOSPC;
+ return (error);
+ }
/*
- * XXXRW: This permission routine appears to be incorrect. If the
- * user matches, we shouldn't go on to the group if the user
- * permissions don't allow the action? Not changed for now. To fix,
- * change from a series of if (); if (); to if () else if () else...
+ * Go ahead and copyout the file descriptor now. This is a bit
+ * premature, but it is a lot easier to handle errors as opposed
+ * to later when we've possibly created a new semaphore, etc.
*/
- uc = td->td_ucred;
- DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
- uc->cr_uid, uc->cr_gid,
- ks->ks_uid, ks->ks_gid, ks->ks_mode));
- if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
- return (0);
- if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
- return (0);
- if ((ks->ks_mode & S_IWOTH) != 0)
- return (0);
- return (priv_check(td, PRIV_SEM_WRITE));
-}
+ semid = fd;
+ error = copyout(&semid, semidp, sizeof(semid));
+ if (error) {
+ fdclose(fdp, fp, fd, td);
+ fdrop(fp, td);
+ return (error);
+ }
-static void
-sem_free(struct ksem *ks)
-{
+ if (name == NULL) {
+ /* Create an anonymous semaphore. */
+ ks = ksem_alloc(td->td_ucred, mode, value);
+ if (ks == NULL)
+ error = ENOSPC;
+ else
+ ks->ks_flags |= KS_ANONYMOUS;
+ } else {
+ path = malloc(MAXPATHLEN, M_KSEM, M_WAITOK);
+ error = copyinstr(name, path, MAXPATHLEN, NULL);
+ /* Require paths to start with a '/' character. */
+ if (error == 0 && path[0] != '/')
+ error = EINVAL;
+ if (error) {
+ fdclose(fdp, fp, fd, td);
+ fdrop(fp, td);
+ free(path, M_KSEM);
+ return (error);
+ }
+
+ fnv = fnv_32_str(path, FNV1_32_INIT);
+ sx_xlock(&ksem_dict_lock);
+ ks = ksem_lookup(path, fnv);
+ if (ks == NULL) {
+ /* Object does not exist, create it if requested. */
+ if (flags & O_CREAT) {
+ ks = ksem_alloc(td->td_ucred, mode, value);
+ if (ks == NULL)
+ error = ENFILE;
+ else {
+ ksem_insert(path, fnv, ks);
+ path = NULL;
+ }
+ } else
+ error = ENOENT;
+ } else {
+ /*
+ * Object already exists, obtain a new
+ * reference if requested and permitted.
+ */
+ if ((flags & (O_CREAT | O_EXCL)) ==
+ (O_CREAT | O_EXCL))
+ error = EEXIST;
+ else {
#ifdef MAC
- mac_posixsem_destroy(ks);
+ error = mac_posixsem_check_open(td->td_ucred,
+ ks);
+ if (error == 0)
#endif
- nsems--;
- if (ks->ks_onlist)
- LIST_REMOVE(ks, ks_entry);
- if (ks->ks_name != NULL)
- free(ks->ks_name, M_SEM);
- cv_destroy(&ks->ks_cv);
- free(ks, M_SEM);
-}
+ error = ksem_access(ks, td->td_ucred);
+ }
+ if (error == 0)
+ ksem_hold(ks);
+#ifdef INVARIANTS
+ else
+ ks = NULL;
+#endif
+ }
+ sx_xunlock(&ksem_dict_lock);
+ if (path)
+ free(path, M_KSEM);
+ }
-static __inline struct kuser *
-sem_getuser(struct proc *p, struct ksem *ks)
-{
- struct kuser *k;
+ if (error) {
+ KASSERT(ks == NULL, ("ksem_create error with a ksem"));
+ fdclose(fdp, fp, fd, td);
+ fdrop(fp, td);
+ return (error);
+ }
+ KASSERT(ks != NULL, ("ksem_create w/o a ksem"));
- LIST_FOREACH(k, &ks->ks_users, ku_next)
- if (k->ku_pid == p->p_pid)
- return (k);
- return (NULL);
-}
+ finit(fp, FREAD | FWRITE, DTYPE_SEM, ks, &ksem_ops);
-static int
-sem_hasopen(struct thread *td, struct ksem *ks)
-{
-
- return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
- || sem_getuser(td->td_proc, ks) != NULL);
+ FILEDESC_XLOCK(fdp);
+ if (fdp->fd_ofiles[fd] == fp)
+ fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
+ FILEDESC_XUNLOCK(fdp);
+ fdrop(fp, td);
+
+ return (0);
}
static int
-sem_leave(struct proc *p, struct ksem *ks)
+ksem_get(struct thread *td, semid_t id, struct file **fpp)
{
- struct kuser *k;
-
- DP(("sem_leave: ks = %p\n", ks));
- k = sem_getuser(p, ks);
- DP(("sem_leave: ks = %p, k = %p\n", ks, k));
- if (k != NULL) {
- LIST_REMOVE(k, ku_next);
- sem_rel(ks);
- DP(("sem_leave: about to free k\n"));
- free(k, M_SEM);
- DP(("sem_leave: returning\n"));
- return (0);
+ struct ksem *ks;
+ struct file *fp;
+ int error;
+
+ error = fget(td, id, &fp);
+ if (error)
+ return (EINVAL);
+ if (fp->f_type != DTYPE_SEM) {
+ fdrop(fp, td);
+ return (EINVAL);
}
- return (EINVAL);
+ ks = fp->f_data;
+ if (ks->ks_flags & KS_DEAD) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ *fpp = fp;
+ return (0);
}
-static void
-sem_enter(struct proc *p, struct ksem *ks)
+/* System calls. */
+#ifndef _SYS_SYSPROTO_H_
+struct ksem_init_args {
+ unsigned int value;
+ semid_t *idp;
+};
+#endif
+int
+ksem_init(struct thread *td, struct ksem_init_args *uap)
{
- struct kuser *ku, *k;
- ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
- ku->ku_pid = p->p_pid;
- mtx_lock(&sem_lock);
- k = sem_getuser(p, ks);
- if (k != NULL) {
- mtx_unlock(&sem_lock);
- free(ku, M_TEMP);
- return;
- }
- LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
- sem_ref(ks);
- mtx_unlock(&sem_lock);
+ return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value,
+ 0));
}
#ifndef _SYS_SYSPROTO_H_
-struct ksem_unlink_args {
- char *name;
+struct ksem_open_args {
+ char *name;
+ int oflag;
+ mode_t mode;
+ unsigned int value;
+ semid_t *idp;
};
-int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
#endif
int
-ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
+ksem_open(struct thread *td, struct ksem_open_args *uap)
{
- char name[SEM_MAX_NAMELEN + 1];
- size_t done;
- int error;
- error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
- return (error ? error :
- kern_sem_unlink(td, name));
+ if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0)
+ return (EINVAL);
+ return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value,
+ uap->oflag));
}
-static int
-kern_sem_unlink(struct thread *td, const char *name)
+#ifndef _SYS_SYSPROTO_H_
+struct ksem_unlink_args {
+ char *name;
+};
+#endif
+int
+ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
{
- struct ksem *ks;
+ char *path;
+ Fnv32_t fnv;
int error;
- mtx_lock(&sem_lock);
- ks = sem_lookup_byname(name);
- if (ks != NULL) {
-#ifdef MAC
- error = mac_posixsem_check_unlink(td->td_ucred, ks);
- if (error) {
- mtx_unlock(&sem_lock);
- return (error);
- }
-#endif
- error = sem_perm(td, ks);
- } else
- error = ENOENT;
- DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
- if (error == 0) {
- LIST_REMOVE(ks, ks_entry);
- LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
- sem_rel(ks);
+ path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+ error = copyinstr(uap->name, path, MAXPATHLEN, NULL);
+ if (error) {
+ free(path, M_TEMP);
+ return (error);
}
- mtx_unlock(&sem_lock);
+
+ fnv = fnv_32_str(path, FNV1_32_INIT);
+ sx_xlock(&ksem_dict_lock);
+ error = ksem_remove(path, fnv, td->td_ucred);
+ sx_xunlock(&ksem_dict_lock);
+ free(path, M_TEMP);
+
return (error);
}
#ifndef _SYS_SYSPROTO_H_
struct ksem_close_args {
- semid_t id;
+ semid_t id;
};
-int ksem_close(struct thread *td, struct ksem_close_args *uap);
#endif
int
ksem_close(struct thread *td, struct ksem_close_args *uap)
{
-
- return (kern_sem_close(td, uap->id));
-}
-
-static int
-kern_sem_close(struct thread *td, semid_t id)
-{
struct ksem *ks;
+ struct file *fp;
int error;
- error = EINVAL;
- mtx_lock(&sem_lock);
- ks = ID_TO_SEM(id);
-
- /*
- * This is not a valid operation for unnamed sems.
- */
- if (ks != NULL && ks->ks_name != NULL)
- error = sem_leave(td->td_proc, ks);
- mtx_unlock(&sem_lock);
+ error = ksem_get(td, uap->id, &fp);
+ if (error)
+ return (error);
+ ks = fp->f_data;
+ if (ks->ks_flags & KS_ANONYMOUS) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ error = kern_close(td, uap->id);
+ fdrop(fp, td);
return (error);
}
#ifndef _SYS_SYSPROTO_H_
struct ksem_post_args {
- semid_t id;
+ semid_t id;
};
-int ksem_post(struct thread *td, struct ksem_post_args *uap);
#endif
int
ksem_post(struct thread *td, struct ksem_post_args *uap)
{
-
- return (kern_sem_post(td, uap->id));
-}
-
-static int
-kern_sem_post(struct thread *td, semid_t id)
-{
+ struct file *fp;
struct ksem *ks;
int error;
+ error = ksem_get(td, uap->id, &fp);
+ if (error)
+ return (error);
+ ks = fp->f_data;
+
mtx_lock(&sem_lock);
- ks = ID_TO_SEM(id);
- if (ks == NULL || !sem_hasopen(td, ks)) {
- error = EINVAL;
- goto err;
- }
#ifdef MAC
- error = mac_posixsem_check_post(td->td_ucred, ks);
+ error = mac_posixsem_check_post(td->td_ucred, fp->f_cred, ks);
if (error)
goto err;
#endif
@@ -628,16 +638,17 @@ kern_sem_post(struct thread *td, semid_t id)
if (ks->ks_waiters > 0)
cv_signal(&ks->ks_cv);
error = 0;
+ vfs_timestamp(&ks->ks_ctime);
err:
mtx_unlock(&sem_lock);
+ fdrop(fp, td);
return (error);
}
#ifndef _SYS_SYSPROTO_H_
struct ksem_wait_args {
- semid_t id;
+ semid_t id;
};
-int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
#endif
int
ksem_wait(struct thread *td, struct ksem_wait_args *uap)
@@ -648,10 +659,9 @@ ksem_wait(struct thread *td, struct ksem_wait_args *uap)
#ifndef _SYS_SYSPROTO_H_
struct ksem_timedwait_args {
- semid_t id;
+ semid_t id;
const struct timespec *abstime;
};
-int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
#endif
int
ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
@@ -678,9 +688,8 @@ ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
#ifndef _SYS_SYSPROTO_H_
struct ksem_trywait_args {
- semid_t id;
+ semid_t id;
};
-int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
#endif
int
ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
@@ -695,31 +704,25 @@ kern_sem_wait(struct thread *td, semid_t id, int tryflag,
{
struct timespec ts1, ts2;
struct timeval tv;
+ struct file *fp;
struct ksem *ks;
int error;
DP((">>> kern_sem_wait entered!\n"));
+ error = ksem_get(td, id, &fp);
+ if (error)
+ return (error);
+ ks = fp->f_data;
mtx_lock(&sem_lock);
- ks = ID_TO_SEM(id);
- if (ks == NULL) {
- DP(("kern_sem_wait ks == NULL\n"));
- error = EINVAL;
- goto err;
- }
- sem_ref(ks);
- if (!sem_hasopen(td, ks)) {
- DP(("kern_sem_wait hasopen failed\n"));
- error = EINVAL;
- goto err;
- }
#ifdef MAC
- error = mac_posixsem_check_wait(td->td_ucred, ks);
+ error = mac_posixsem_check_wait(td->td_ucred, fp->f_cred, ks);
if (error) {
DP(("kern_sem_wait mac failed\n"));
goto err;
}
#endif
DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
+ vfs_timestamp(&ks->ks_atime);
if (ks->ks_value == 0) {
ks->ks_waiters++;
if (tryflag != 0)
@@ -749,208 +752,159 @@ kern_sem_wait(struct thread *td, semid_t id, int tryflag,
ks->ks_value--;
error = 0;
err:
- if (ks != NULL)
- sem_rel(ks);
mtx_unlock(&sem_lock);
+ fdrop(fp, td);
DP(("<<< kern_sem_wait leaving, error = %d\n", error));
return (error);
}
#ifndef _SYS_SYSPROTO_H_
struct ksem_getvalue_args {
- semid_t id;
- int *val;
+ semid_t id;
+ int *val;
};
-int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
#endif
int
ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
{
+ struct file *fp;
struct ksem *ks;
int error, val;
+ error = ksem_get(td, uap->id, &fp);
+ if (error)
+ return (error);
+ ks = fp->f_data;
+
mtx_lock(&sem_lock);
- ks = ID_TO_SEM(uap->id);
- if (ks == NULL || !sem_hasopen(td, ks)) {
- mtx_unlock(&sem_lock);
- return (EINVAL);
- }
#ifdef MAC
- error = mac_posixsem_check_getvalue(td->td_ucred, ks);
+ error = mac_posixsem_check_getvalue(td->td_ucred, fp->f_cred, ks);
if (error) {
mtx_unlock(&sem_lock);
+ fdrop(fp, td);
return (error);
}
#endif
val = ks->ks_value;
+ vfs_timestamp(&ks->ks_atime);
mtx_unlock(&sem_lock);
+ fdrop(fp, td);
error = copyout(&val, uap->val, sizeof(val));
return (error);
}
#ifndef _SYS_SYSPROTO_H_
struct ksem_destroy_args {
- semid_t id;
+ semid_t id;
};
-int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
#endif
int
ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
{
+ struct file *fp;
struct ksem *ks;
int error;
- mtx_lock(&sem_lock);
- ks = ID_TO_SEM(uap->id);
- if (ks == NULL || !sem_hasopen(td, ks) ||
- ks->ks_name != NULL) {
- error = EINVAL;
- goto err;
+ error = ksem_get(td, uap->id, &fp);
+ if (error)
+ return (error);
+ ks = fp->f_data;
+ if (!(ks->ks_flags & KS_ANONYMOUS)) {
+ fdrop(fp, td);
+ return (EINVAL);
}
+ mtx_lock(&sem_lock);
if (ks->ks_waiters != 0) {
+ mtx_unlock(&sem_lock);
error = EBUSY;
goto err;
}
- sem_rel(ks);
- error = 0;
-err:
+ ks->ks_flags |= KS_DEAD;
mtx_unlock(&sem_lock);
+
+ error = kern_close(td, uap->id);
+err:
+ fdrop(fp, td);
return (error);
}
-/*
- * Count the number of kusers associated with a proc, so as to guess at how
- * many to allocate when forking.
- */
-static int
-sem_count_proc(struct proc *p)
-{
- struct ksem *ks;
- struct kuser *ku;
- int count;
-
- mtx_assert(&sem_lock, MA_OWNED);
+#define SYSCALL_DATA(syscallname) \
+static int syscallname##_syscall = SYS_##syscallname; \
+static int syscallname##_registered; \
+static struct sysent syscallname##_old_sysent; \
+MAKE_SYSENT(syscallname);
+
+#define SYSCALL_REGISTER(syscallname) do { \
+ error = syscall_register(& syscallname##_syscall, \
+ & syscallname##_sysent, & syscallname##_old_sysent); \
+ if (error) \
+ return (error); \
+ syscallname##_registered = 1; \
+} while(0)
+
+#define SYSCALL_DEREGISTER(syscallname) do { \
+ if (syscallname##_registered) { \
+ syscallname##_registered = 0; \
+ syscall_deregister(& syscallname##_syscall, \
+ & syscallname##_old_sysent); \
+ } \
+} while(0)
+
+SYSCALL_DATA(ksem_init);
+SYSCALL_DATA(ksem_open);
+SYSCALL_DATA(ksem_unlink);
+SYSCALL_DATA(ksem_close);
+SYSCALL_DATA(ksem_post);
+SYSCALL_DATA(ksem_wait);
+SYSCALL_DATA(ksem_timedwait);
+SYSCALL_DATA(ksem_trywait);
+SYSCALL_DATA(ksem_getvalue);
+SYSCALL_DATA(ksem_destroy);
- count = 0;
- LIST_FOREACH(ks, &ksem_head, ks_entry) {
- LIST_FOREACH(ku, &ks->ks_users, ku_next) {
- if (ku->ku_pid == p->p_pid)
- count++;
- }
- }
- LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
- LIST_FOREACH(ku, &ks->ks_users, ku_next) {
- if (ku->ku_pid == p->p_pid)
- count++;
- }
- }
- return (count);
-}
-
-/*
- * When a process forks, the child process must gain a reference to each open
- * semaphore in the parent process, whether it is unlinked or not. This
- * requires allocating a kuser structure for each semaphore reference in the
- * new process. Because the set of semaphores in the parent can change while
- * the fork is in progress, we have to handle races -- first we attempt to
- * allocate enough storage to acquire references to each of the semaphores,
- * then we enter the semaphores and release the temporary references.
- */
-static void
-sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
+static int
+ksem_module_init(void)
{
- struct ksem *ks, **sem_array;
- int count, i, new_count;
- struct kuser *ku;
-
- mtx_lock(&sem_lock);
- count = sem_count_proc(p1);
- if (count == 0) {
- mtx_unlock(&sem_lock);
- return;
- }
-race_lost:
- mtx_assert(&sem_lock, MA_OWNED);
- mtx_unlock(&sem_lock);
- sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
- mtx_lock(&sem_lock);
- new_count = sem_count_proc(p1);
- if (count < new_count) {
- /* Lost race, repeat and allocate more storage. */
- free(sem_array, M_TEMP);
- count = new_count;
- goto race_lost;
- }
-
- /*
- * Given an array capable of storing an adequate number of semaphore
- * references, now walk the list of semaphores and acquire a new
- * reference for any semaphore opened by p1.
- */
- count = new_count;
- i = 0;
- LIST_FOREACH(ks, &ksem_head, ks_entry) {
- LIST_FOREACH(ku, &ks->ks_users, ku_next) {
- if (ku->ku_pid == p1->p_pid) {
- sem_ref(ks);
- sem_array[i] = ks;
- i++;
- break;
- }
- }
- }
- LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
- LIST_FOREACH(ku, &ks->ks_users, ku_next) {
- if (ku->ku_pid == p1->p_pid) {
- sem_ref(ks);
- sem_array[i] = ks;
- i++;
- break;
- }
- }
- }
- mtx_unlock(&sem_lock);
- KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
+ int error;
- /*
- * Now cause p2 to enter each of the referenced semaphores, then
- * release our temporary reference. This is pretty inefficient.
- * Finally, free our temporary array.
- */
- for (i = 0; i < count; i++) {
- sem_enter(p2, sem_array[i]);
- mtx_lock(&sem_lock);
- sem_rel(sem_array[i]);
- mtx_unlock(&sem_lock);
- }
- free(sem_array, M_TEMP);
+ mtx_init(&sem_lock, "sem", NULL, MTX_DEF);
+ mtx_init(&ksem_count_lock, "ksem count", NULL, MTX_DEF);
+ sx_init(&ksem_dict_lock, "ksem dictionary");
+ ksem_dictionary = hashinit(1024, M_KSEM, &ksem_hash);
+ p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
+ p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
+
+ SYSCALL_REGISTER(ksem_init);
+ SYSCALL_REGISTER(ksem_open);
+ SYSCALL_REGISTER(ksem_unlink);
+ SYSCALL_REGISTER(ksem_close);
+ SYSCALL_REGISTER(ksem_post);
+ SYSCALL_REGISTER(ksem_wait);
+ SYSCALL_REGISTER(ksem_timedwait);
+ SYSCALL_REGISTER(ksem_trywait);
+ SYSCALL_REGISTER(ksem_getvalue);
+ SYSCALL_REGISTER(ksem_destroy);
+ return (0);
}
static void
-sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
+ksem_module_destroy(void)
{
- sem_exithook(arg, p);
-}
-static void
-sem_exithook(void *arg, struct proc *p)
-{
- struct ksem *ks, *ksnext;
+ SYSCALL_DEREGISTER(ksem_init);
+ SYSCALL_DEREGISTER(ksem_open);
+ SYSCALL_DEREGISTER(ksem_unlink);
+ SYSCALL_DEREGISTER(ksem_close);
+ SYSCALL_DEREGISTER(ksem_post);
+ SYSCALL_DEREGISTER(ksem_wait);
+ SYSCALL_DEREGISTER(ksem_timedwait);
+ SYSCALL_DEREGISTER(ksem_trywait);
+ SYSCALL_DEREGISTER(ksem_getvalue);
+ SYSCALL_DEREGISTER(ksem_destroy);
- mtx_lock(&sem_lock);
- ks = LIST_FIRST(&ksem_head);
- while (ks != NULL) {
- ksnext = LIST_NEXT(ks, ks_entry);
- sem_leave(p, ks);
- ks = ksnext;
- }
- ks = LIST_FIRST(&ksem_deadhead);
- while (ks != NULL) {
- ksnext = LIST_NEXT(ks, ks_entry);
- sem_leave(p, ks);
- ks = ksnext;
- }
- mtx_unlock(&sem_lock);
+ hashdestroy(ksem_dictionary, M_KSEM, ksem_hash);
+ sx_destroy(&ksem_dict_lock);
+ mtx_destroy(&ksem_count_lock);
+ mtx_destroy(&sem_lock);
}
static int
@@ -960,26 +914,21 @@ sem_modload(struct module *module, int cmd, void *arg)
switch (cmd) {
case MOD_LOAD:
- mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
- p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
- p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
- sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
- sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
- sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
- sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
- sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
- sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
+ error = ksem_module_init();
+ if (error)
+ ksem_module_destroy();
break;
case MOD_UNLOAD:
+ mtx_lock(&ksem_count_lock);
if (nsems != 0) {
error = EOPNOTSUPP;
+ mtx_unlock(&ksem_count_lock);
break;
}
- EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
- EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
- EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
- mtx_destroy(&sem_lock);
+ ksem_dead = 1;
+ mtx_unlock(&ksem_count_lock);
+ ksem_module_destroy();
break;
case MOD_SHUTDOWN:
@@ -997,16 +946,5 @@ static moduledata_t sem_mod = {
NULL
};
-SYSCALL_MODULE_HELPER(ksem_init);
-SYSCALL_MODULE_HELPER(ksem_open);
-SYSCALL_MODULE_HELPER(ksem_unlink);
-SYSCALL_MODULE_HELPER(ksem_close);
-SYSCALL_MODULE_HELPER(ksem_post);
-SYSCALL_MODULE_HELPER(ksem_wait);
-SYSCALL_MODULE_HELPER(ksem_timedwait);
-SYSCALL_MODULE_HELPER(ksem_trywait);
-SYSCALL_MODULE_HELPER(ksem_getvalue);
-SYSCALL_MODULE_HELPER(ksem_destroy);
-
DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
MODULE_VERSION(sem, 1);
diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 899896b..577a8fe 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -46,6 +46,10 @@
* worry about the bits on disk if the page is swapped out or will the
* swapper zero the parts of a page that are invalid if the page is
* swapped back in for us?
+ *
+ * (6) Add MAC support in mac_biba(4) and mac_mls(4).
+ *
+ * (7) Add a MAC check_create() hook for creating new named objects.
*/
#include <sys/cdefs.h>
diff --git a/sys/modules/sem/Makefile b/sys/modules/sem/Makefile
index 8b88444..26178b0 100644
--- a/sys/modules/sem/Makefile
+++ b/sys/modules/sem/Makefile
@@ -3,6 +3,6 @@
.PATH: ${.CURDIR}/../../kern
KMOD= sem
-SRCS= uipc_sem.c opt_mac.h opt_posix.h
+SRCS= uipc_sem.c opt_mac.h opt_posix.h vnode_if.h
.include <bsd.kmod.mk>
diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h
index c68d2d1..2a8b00c 100644
--- a/sys/security/mac/mac_framework.h
+++ b/sys/security/mac/mac_framework.h
@@ -189,11 +189,16 @@ void mac_pipe_init(struct pipepair *);
int mac_pipe_label_set(struct ucred *cred, struct pipepair *pp,
struct label *label);
-int mac_posixsem_check_getvalue(struct ucred *cred,struct ksem *ks);
+int mac_posixsem_check_getvalue(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks);
int mac_posixsem_check_open(struct ucred *cred, struct ksem *ks);
-int mac_posixsem_check_post(struct ucred *cred, struct ksem *ks);
+int mac_posixsem_check_post(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks);
+int mac_posixsem_check_stat(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks);
int mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks);
-int mac_posixsem_check_wait(struct ucred *cred, struct ksem *ks);
+int mac_posixsem_check_wait(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks);
void mac_posixsem_create(struct ucred *cred, struct ksem *ks);
void mac_posixsem_destroy(struct ksem *);
void mac_posixsem_init(struct ksem *);
diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h
index f0fa755..532ca24 100644
--- a/sys/security/mac/mac_policy.h
+++ b/sys/security/mac/mac_policy.h
@@ -288,16 +288,22 @@ typedef int (*mpo_pipe_internalize_label_t)(struct label *label,
typedef void (*mpo_pipe_relabel_t)(struct ucred *cred, struct pipepair *pp,
struct label *oldlabel, struct label *newlabel);
-typedef int (*mpo_posixsem_check_getvalue_t)(struct ucred *cred,
- struct ksem *ks, struct label *kslabel);
+typedef int (*mpo_posixsem_check_getvalue_t)(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks,
+ struct label *kslabel);
typedef int (*mpo_posixsem_check_open_t)(struct ucred *cred,
struct ksem *ks, struct label *kslabel);
-typedef int (*mpo_posixsem_check_post_t)(struct ucred *cred,
- struct ksem *ks, struct label *kslabel);
+typedef int (*mpo_posixsem_check_post_t)(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks,
+ struct label *kslabel);
+typedef int (*mpo_posixsem_check_stat_t)(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks,
+ struct label *kslabel);
typedef int (*mpo_posixsem_check_unlink_t)(struct ucred *cred,
struct ksem *ks, struct label *kslabel);
-typedef int (*mpo_posixsem_check_wait_t)(struct ucred *cred,
- struct ksem *ks, struct label *kslabel);
+typedef int (*mpo_posixsem_check_wait_t)(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks,
+ struct label *kslabel);
typedef void (*mpo_posixsem_create_t)(struct ucred *cred,
struct ksem *ks, struct label *kslabel);
typedef void (*mpo_posixsem_destroy_label_t)(struct label *label);
@@ -742,6 +748,7 @@ struct mac_policy_ops {
mpo_posixsem_check_getvalue_t mpo_posixsem_check_getvalue;
mpo_posixsem_check_open_t mpo_posixsem_check_open;
mpo_posixsem_check_post_t mpo_posixsem_check_post;
+ mpo_posixsem_check_stat_t mpo_posixsem_check_stat;
mpo_posixsem_check_unlink_t mpo_posixsem_check_unlink;
mpo_posixsem_check_wait_t mpo_posixsem_check_wait;
mpo_posixsem_create_t mpo_posixsem_create;
diff --git a/sys/security/mac/mac_posix_sem.c b/sys/security/mac/mac_posix_sem.c
index 68fb56c..2296afe 100644
--- a/sys/security/mac/mac_posix_sem.c
+++ b/sys/security/mac/mac_posix_sem.c
@@ -101,21 +101,37 @@ mac_posixsem_check_open(struct ucred *cred, struct ksem *ks)
}
int
-mac_posixsem_check_getvalue(struct ucred *cred, struct ksem *ks)
+mac_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks)
{
int error;
- MAC_CHECK(posixsem_check_getvalue, cred, ks, ks->ks_label);
+ MAC_CHECK(posixsem_check_getvalue, active_cred, file_cred, ks,
+ ks->ks_label);
return (error);
}
int
-mac_posixsem_check_post(struct ucred *cred, struct ksem *ks)
+mac_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks)
{
int error;
- MAC_CHECK(posixsem_check_post, cred, ks, ks->ks_label);
+ MAC_CHECK(posixsem_check_post, active_cred, file_cred, ks,
+ ks->ks_label);
+
+ return (error);
+}
+
+int
+mac_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks)
+{
+ int error;
+
+ MAC_CHECK(posixsem_check_stat, active_cred, file_cred, ks,
+ ks->ks_label);
return (error);
}
@@ -131,11 +147,13 @@ mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks)
}
int
-mac_posixsem_check_wait(struct ucred *cred, struct ksem *ks)
+mac_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks)
{
int error;
- MAC_CHECK(posixsem_check_wait, cred, ks, ks->ks_label);
+ MAC_CHECK(posixsem_check_wait, active_cred, file_cred, ks,
+ ks->ks_label);
return (error);
}
diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c
index 4c0c85b..960591c 100644
--- a/sys/security/mac_biba/mac_biba.c
+++ b/sys/security/mac_biba/mac_biba.c
@@ -1504,7 +1504,7 @@ biba_pipe_relabel(struct ucred *cred, struct pipepair *pp,
}
static int
-biba_posixsem_check_write(struct ucred *cred, struct ksem *ks,
+biba_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks,
struct label *kslabel)
{
struct mac_biba *subj, *obj;
@@ -1522,15 +1522,33 @@ biba_posixsem_check_write(struct ucred *cred, struct ksem *ks,
}
static int
-biba_posixsem_check_rdonly(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+biba_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
struct mac_biba *subj, *obj;
if (!biba_enabled)
return (0);
- subj = SLOT(cred->cr_label);
+ subj = SLOT(active_cred->cr_label);
+ obj = SLOT(kslabel);
+
+ if (!biba_dominate_effective(subj, obj))
+ return (EACCES);
+
+ return (0);
+}
+
+static int
+biba_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
+{
+ struct mac_biba *subj, *obj;
+
+ if (!biba_enabled)
+ return (0);
+
+ subj = SLOT(active_cred->cr_label);
obj = SLOT(kslabel);
if (!biba_dominate_effective(obj, subj))
@@ -3335,9 +3353,10 @@ static struct mac_policy_ops mac_biba_ops =
.mpo_pipe_relabel = biba_pipe_relabel,
.mpo_posixsem_check_getvalue = biba_posixsem_check_rdonly,
- .mpo_posixsem_check_open = biba_posixsem_check_write,
+ .mpo_posixsem_check_open = biba_posixsem_check_openunlink,
.mpo_posixsem_check_post = biba_posixsem_check_write,
- .mpo_posixsem_check_unlink = biba_posixsem_check_write,
+ .mpo_posixsem_check_stat = biba_posixsem_check_rdonly,
+ .mpo_posixsem_check_unlink = biba_posixsem_check_openunlink,
.mpo_posixsem_check_wait = biba_posixsem_check_write,
.mpo_posixsem_create = biba_posixsem_create,
.mpo_posixsem_destroy_label = biba_destroy_label,
diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c
index cff9d88..34c618c 100644
--- a/sys/security/mac_mls/mac_mls.c
+++ b/sys/security/mac_mls/mac_mls.c
@@ -1400,7 +1400,7 @@ mls_pipe_relabel(struct ucred *cred, struct pipepair *pp,
}
static int
-mls_posixsem_check_rdonly(struct ucred *cred, struct ksem *ks,
+mls_posixsem_check_openunlink(struct ucred *cred, struct ksem *ks,
struct label *kslabel)
{
struct mac_mls *subj, *obj;
@@ -1411,6 +1411,24 @@ mls_posixsem_check_rdonly(struct ucred *cred, struct ksem *ks,
subj = SLOT(cred->cr_label);
obj = SLOT(kslabel);
+ if (!mls_dominate_effective(obj, subj))
+ return (EACCES);
+
+ return (0);
+}
+
+static int
+mls_posixsem_check_rdonly(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
+{
+ struct mac_mls *subj, *obj;
+
+ if (!mls_enabled)
+ return (0);
+
+ subj = SLOT(active_cred->cr_label);
+ obj = SLOT(kslabel);
+
if (!mls_dominate_effective(subj, obj))
return (EACCES);
@@ -1418,15 +1436,15 @@ mls_posixsem_check_rdonly(struct ucred *cred, struct ksem *ks,
}
static int
-mls_posixsem_check_write(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+mls_posixsem_check_write(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
struct mac_mls *subj, *obj;
if (!mls_enabled)
return (0);
- subj = SLOT(cred->cr_label);
+ subj = SLOT(active_cred->cr_label);
obj = SLOT(kslabel);
if (!mls_dominate_effective(obj, subj))
@@ -2958,9 +2976,10 @@ static struct mac_policy_ops mls_ops =
.mpo_pipe_relabel = mls_pipe_relabel,
.mpo_posixsem_check_getvalue = mls_posixsem_check_rdonly,
- .mpo_posixsem_check_open = mls_posixsem_check_write,
+ .mpo_posixsem_check_open = mls_posixsem_check_openunlink,
.mpo_posixsem_check_post = mls_posixsem_check_write,
- .mpo_posixsem_check_unlink = mls_posixsem_check_write,
+ .mpo_posixsem_check_stat = mls_posixsem_check_rdonly,
+ .mpo_posixsem_check_unlink = mls_posixsem_check_openunlink,
.mpo_posixsem_check_wait = mls_posixsem_check_write,
.mpo_posixsem_create = mls_posixsem_create,
.mpo_posixsem_destroy_label = mls_destroy_label,
diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c
index 34f5cad..36339b5 100644
--- a/sys/security/mac_stub/mac_stub.c
+++ b/sys/security/mac_stub/mac_stub.c
@@ -523,8 +523,8 @@ stub_pipe_relabel(struct ucred *cred, struct pipepair *pp,
}
static int
-stub_posixsem_check_getvalue(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+stub_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
return (0);
@@ -539,8 +539,16 @@ stub_posixsem_check_open(struct ucred *cred, struct ksem *ks,
}
static int
-stub_posixsem_check_post(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+stub_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
+{
+
+ return (0);
+}
+
+static int
+stub_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
return (0);
@@ -555,8 +563,8 @@ stub_posixsem_check_unlink(struct ucred *cred, struct ksem *ks,
}
static int
-stub_posixsem_check_wait(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+stub_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
return (0);
@@ -1582,6 +1590,7 @@ static struct mac_policy_ops stub_ops =
.mpo_posixsem_check_getvalue = stub_posixsem_check_getvalue,
.mpo_posixsem_check_open = stub_posixsem_check_open,
.mpo_posixsem_check_post = stub_posixsem_check_post,
+ .mpo_posixsem_check_stat = stub_posixsem_check_stat,
.mpo_posixsem_check_unlink = stub_posixsem_check_unlink,
.mpo_posixsem_check_wait = stub_posixsem_check_wait,
.mpo_posixsem_create = stub_posixsem_create,
diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c
index c25e937..5e788b9 100644
--- a/sys/security/mac_test/mac_test.c
+++ b/sys/security/mac_test/mac_test.c
@@ -1012,11 +1012,12 @@ test_pipe_relabel(struct ucred *cred, struct pipepair *pp,
COUNTER_DECL(posixsem_check_getvalue);
static int
-test_posixsem_check_getvalue(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+test_posixsem_check_getvalue(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
- LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(active_cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(file_cred->cr_label, MAGIC_CRED);
LABEL_CHECK(kslabel, MAGIC_POSIX_SEM);
COUNTER_INC(posixsem_check_getvalue);
@@ -1038,17 +1039,31 @@ test_posixsem_check_open(struct ucred *cred, struct ksem *ks,
COUNTER_DECL(posixsem_check_post);
static int
-test_posixsem_check_post(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+test_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
- LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(active_cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(file_cred->cr_label, MAGIC_CRED);
LABEL_CHECK(kslabel, MAGIC_POSIX_SEM);
COUNTER_INC(posixsem_check_post);
return (0);
}
+COUNTER_DECL(posixsem_check_stat);
+static int
+test_posixsem_check_stat(struct ucred *active_cred,
+ struct ucred *file_cred, struct ksem *ks, struct label *kslabel)
+{
+
+ LABEL_CHECK(active_cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(file_cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(kslabel, MAGIC_POSIX_SEM);
+ COUNTER_INC(posixsem_check_stat);
+ return (0);
+}
+
COUNTER_DECL(posixsem_check_unlink);
static int
test_posixsem_check_unlink(struct ucred *cred, struct ksem *ks,
@@ -1064,11 +1079,12 @@ test_posixsem_check_unlink(struct ucred *cred, struct ksem *ks,
COUNTER_DECL(posixsem_check_wait);
static int
-test_posixsem_check_wait(struct ucred *cred, struct ksem *ks,
- struct label *kslabel)
+test_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred,
+ struct ksem *ks, struct label *kslabel)
{
- LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(active_cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(file_cred->cr_label, MAGIC_CRED);
LABEL_CHECK(kslabel, MAGIC_POSIX_SEM);
COUNTER_INC(posixsem_check_wait);
@@ -2881,6 +2897,7 @@ static struct mac_policy_ops test_ops =
.mpo_posixsem_check_getvalue = test_posixsem_check_getvalue,
.mpo_posixsem_check_open = test_posixsem_check_open,
.mpo_posixsem_check_post = test_posixsem_check_post,
+ .mpo_posixsem_check_stat = test_posixsem_check_stat,
.mpo_posixsem_check_unlink = test_posixsem_check_unlink,
.mpo_posixsem_check_wait = test_posixsem_check_wait,
.mpo_posixsem_create = test_posixsem_create,
diff --git a/sys/sys/file.h b/sys/sys/file.h
index b2a6820..6f7dc00 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -61,6 +61,7 @@ struct socket;
#define DTYPE_CRYPTO 6 /* crypto */
#define DTYPE_MQUEUE 7 /* posix message queue */
#define DTYPE_SHM 8 /* swap-backed shared memory */
+#define DTYPE_SEM 9 /* posix semaphore */
#ifdef _KERNEL
diff --git a/sys/sys/ksem.h b/sys/sys/ksem.h
index 71979da..da868fa 100644
--- a/sys/sys/ksem.h
+++ b/sys/sys/ksem.h
@@ -34,26 +34,32 @@
#endif
#include <sys/condvar.h>
-#include <sys/queue.h>
-
-struct kuser {
- pid_t ku_pid;
- LIST_ENTRY(kuser) ku_next;
-};
struct ksem {
- LIST_ENTRY(ksem) ks_entry; /* global list entry */
- int ks_onlist; /* boolean if on a list (ks_entry) */
- char *ks_name; /* if named, this is the name */
- int ks_ref; /* number of references */
- mode_t ks_mode; /* protection bits */
- uid_t ks_uid; /* creator uid */
- gid_t ks_gid; /* creator gid */
- unsigned int ks_value; /* current value */
- struct cv ks_cv; /* waiters sleep here */
- int ks_waiters; /* number of waiters */
- LIST_HEAD(, kuser) ks_users; /* pids using this sem */
- struct label *ks_label; /* MAC label */
+ int ks_ref; /* number of references */
+ mode_t ks_mode; /* protection bits */
+ uid_t ks_uid; /* creator uid */
+ gid_t ks_gid; /* creator gid */
+ unsigned int ks_value; /* current value */
+ struct cv ks_cv; /* waiters sleep here */
+ int ks_waiters; /* number of waiters */
+ int ks_flags;
+
+ /*
+ * Values maintained solely to make this a better-behaved file
+ * descriptor for fstat() to run on.
+ *
+ * XXX: dubious
+ */
+ struct timespec ks_atime;
+ struct timespec ks_mtime;
+ struct timespec ks_ctime;
+ struct timespec ks_birthtime;
+
+ struct label *ks_label; /* MAC label */
};
+#define KS_ANONYMOUS 0x0001 /* Anonymous (unnamed) semaphore. */
+#define KS_DEAD 0x0002 /* No new waiters allowed. */
+
#endif /* !_POSIX4_KSEM_H_ */
diff --git a/sys/sys/user.h b/sys/sys/user.h
index a6237f4..e8b6a1f 100644
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
@@ -249,6 +249,7 @@ struct user {
#define KF_TYPE_CRYPTO 6
#define KF_TYPE_MQUEUE 7
#define KF_TYPE_SHM 8
+#define KF_TYPE_SEM 9
#define KF_TYPE_UNKNOWN 255
#define KF_VTYPE_VNON 0
diff --git a/tools/regression/posixsem/Makefile b/tools/regression/posixsem/Makefile
new file mode 100644
index 0000000..f7568f4
--- /dev/null
+++ b/tools/regression/posixsem/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= posixsem
+SRCS= posixsem.c test.c
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+NO_MAN=
+
+WARNS?= 3
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/posixsem/posixsem.c b/tools/regression/posixsem/posixsem.c
new file mode 100644
index 0000000..465d6e7
--- /dev/null
+++ b/tools/regression/posixsem/posixsem.c
@@ -0,0 +1,1437 @@
+/*-
+ * Copyright (c) 2008 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/_semaphore.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "test.h"
+
+/* Cut and pasted from kernel header, bah! */
+
+/* Operations on timespecs */
+#define timespecclear(tvp) ((tvp)->tv_sec = (tvp)->tv_nsec = 0)
+#define timespecisset(tvp) ((tvp)->tv_sec || (tvp)->tv_nsec)
+#define timespeccmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#define timespecadd(vvp, uvp) \
+ do { \
+ (vvp)->tv_sec += (uvp)->tv_sec; \
+ (vvp)->tv_nsec += (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec >= 1000000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_nsec -= 1000000000; \
+ } \
+ } while (0)
+#define timespecsub(vvp, uvp) \
+ do { \
+ (vvp)->tv_sec -= (uvp)->tv_sec; \
+ (vvp)->tv_nsec -= (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_nsec += 1000000000; \
+ } \
+ } while (0)
+
+
+#define TEST_PATH "/tmp/posixsem_regression_test"
+
+#define ELAPSED(elapsed, limit) (abs((elapsed) - (limit)) < 100)
+
+/* Macros for passing child status to parent over a pipe. */
+#define CSTAT(class, error) ((class) << 16 | (error))
+#define CSTAT_CLASS(stat) ((stat) >> 16)
+#define CSTAT_ERROR(stat) ((stat) & 0xffff)
+
+/*
+ * Helper routine for tests that use a child process. This routine
+ * creates a pipe and forks a child process. The child process runs
+ * the 'func' routine which returns a status integer. The status
+ * integer gets written over the pipe to the parent and returned in
+ * '*stat'. If there is an error in pipe(), fork(), or wait() this
+ * returns -1 and fails the test.
+ */
+static int
+child_worker(int (*func)(void *arg), void *arg, int *stat)
+{
+ pid_t pid;
+ int pfd[2], cstat;
+
+ if (pipe(pfd) < 0) {
+ fail_errno("pipe");
+ return (-1);
+ }
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ /* Error. */
+ fail_errno("fork");
+ close(pfd[0]);
+ close(pfd[1]);
+ return (-1);
+ case 0:
+ /* Child. */
+ cstat = func(arg);
+ write(pfd[1], &cstat, sizeof(cstat));
+ exit(0);
+ }
+
+ if (read(pfd[0], stat, sizeof(*stat)) < 0) {
+ fail_errno("read(pipe)");
+ close(pfd[0]);
+ close(pfd[1]);
+ return (-1);
+ }
+ if (waitpid(pid, NULL, 0) < 0) {
+ fail_errno("wait");
+ close(pfd[0]);
+ close(pfd[1]);
+ return (-1);
+ }
+ close(pfd[0]);
+ close(pfd[1]);
+ return (0);
+}
+
+/*
+ * Attempt a ksem_open() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
+ value, int error)
+{
+ semid_t id;
+
+ if (ksem_open(&id, path, flags, mode, value) >= 0) {
+ fail_err("ksem_open() didn't fail");
+ ksem_close(id);
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_open");
+ return;
+ }
+ pass();
+}
+
+/*
+ * Attempt a ksem_unlink() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_unlink_should_fail(const char *path, int error)
+{
+
+ if (ksem_unlink(path) >= 0) {
+ fail_err("ksem_unlink() didn't fail");
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_unlink");
+ return;
+ }
+ pass();
+}
+
+/*
+ * Attempt a ksem_close() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_close_should_fail(semid_t id, int error)
+{
+
+ if (ksem_close(id) >= 0) {
+ fail_err("ksem_close() didn't fail");
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_close");
+ return;
+ }
+ pass();
+}
+
+/*
+ * Attempt a ksem_init() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_init_should_fail(unsigned int value, int error)
+{
+ semid_t id;
+
+ if (ksem_init(&id, value) >= 0) {
+ fail_err("ksem_init() didn't fail");
+ ksem_destroy(id);
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_init");
+ return;
+ }
+ pass();
+}
+
+/*
+ * Attempt a ksem_destroy() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_destroy_should_fail(semid_t id, int error)
+{
+
+ if (ksem_destroy(id) >= 0) {
+ fail_err("ksem_destroy() didn't fail");
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+
+/*
+ * Attempt a ksem_post() that should fail with an expected error of
+ * 'error'.
+ */
+static void
+ksem_post_should_fail(semid_t id, int error)
+{
+
+ if (ksem_post(id) >= 0) {
+ fail_err("ksem_post() didn't fail");
+ return;
+ }
+ if (errno != error) {
+ fail_errno("ksem_post");
+ return;
+ }
+ pass();
+}
+
+static void
+open_after_unlink(void)
+{
+ semid_t id;
+
+ if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
+ fail_errno("ksem_open(1)");
+ return;
+ }
+ ksem_close(id);
+
+ if (ksem_unlink(TEST_PATH) < 0) {
+ fail_errno("ksem_unlink");
+ return;
+ }
+
+ ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
+}
+TEST(open_after_unlink, "open after unlink");
+
+static void
+open_invalid_path(void)
+{
+
+ ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
+}
+TEST(open_invalid_path, "open invalid path");
+
+static void
+open_extra_flags(void)
+{
+
+ ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
+}
+TEST(open_extra_flags, "open with extra flags");
+
+static void
+open_bad_value(void)
+{
+
+ (void)ksem_unlink(TEST_PATH);
+
+ ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
+}
+TEST(open_bad_value, "open with invalid initial value");
+
+static void
+open_bad_path_pointer(void)
+{
+
+ ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
+}
+TEST(open_bad_path_pointer, "open bad path pointer");
+
+static void
+open_path_too_long(void)
+{
+ char *page;
+
+ page = malloc(MAXPATHLEN + 1);
+ memset(page, 'a', MAXPATHLEN);
+ page[MAXPATHLEN] = '\0';
+ ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
+ free(page);
+}
+TEST(open_path_too_long, "open pathname too long");
+
+static void
+open_nonexisting_semaphore(void)
+{
+
+ ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
+}
+TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
+
+static void
+exclusive_create_existing_semaphore(void)
+{
+ semid_t id;
+
+ if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
+ fail_errno("ksem_open(O_CREAT)");
+ return;
+ }
+ ksem_close(id);
+
+ ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
+
+ ksem_unlink(TEST_PATH);
+}
+TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
+
+static void
+init_bad_value(void)
+{
+
+ ksem_init_should_fail(UINT_MAX, EINVAL);
+}
+TEST(init_bad_value, "init with invalid initial value");
+
+static void
+unlink_bad_path_pointer(void)
+{
+
+ ksem_unlink_should_fail((char *)1024, EFAULT);
+}
+TEST(unlink_bad_path_pointer, "unlink bad path pointer");
+
+static void
+unlink_path_too_long(void)
+{
+ char *page;
+
+ page = malloc(MAXPATHLEN + 1);
+ memset(page, 'a', MAXPATHLEN);
+ page[MAXPATHLEN] = '\0';
+ ksem_unlink_should_fail(page, ENAMETOOLONG);
+ free(page);
+}
+TEST(unlink_path_too_long, "unlink pathname too long");
+
+static void
+destroy_named_semaphore(void)
+{
+ semid_t id;
+
+ if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
+ fail_errno("ksem_open(O_CREAT)");
+ return;
+ }
+
+ ksem_destroy_should_fail(id, EINVAL);
+
+ ksem_close(id);
+ ksem_unlink(TEST_PATH);
+}
+TEST(destroy_named_semaphore, "destroy named semaphore");
+
+static void
+close_unnamed_semaphore(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ ksem_close_should_fail(id, EINVAL);
+
+ ksem_destroy(id);
+}
+TEST(close_unnamed_semaphore, "close unnamed semaphore");
+
+static void
+destroy_invalid_fd(void)
+{
+
+ ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
+}
+TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
+
+static void
+close_invalid_fd(void)
+{
+
+ ksem_close_should_fail(STDERR_FILENO, EINVAL);
+}
+TEST(close_invalid_fd, "close non-semaphore file descriptor");
+
+static void
+create_unnamed_semaphore(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(create_unnamed_semaphore, "create unnamed semaphore");
+
+static void
+open_named_semaphore(void)
+{
+ semid_t id;
+
+ if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
+ fail_errno("ksem_open(O_CREAT)");
+ return;
+ }
+
+ if (ksem_close(id) < 0) {
+ fail_errno("ksem_close");
+ return;
+ }
+
+ if (ksem_unlink(TEST_PATH) < 0) {
+ fail_errno("ksem_unlink");
+ return;
+ }
+ pass();
+}
+TEST(open_named_semaphore, "create named semaphore");
+
+static void
+getvalue_invalid_semaphore(void)
+{
+ int val;
+
+ if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
+ fail_err("ksem_getvalue() didn't fail");
+ return;
+ }
+ if (errno != EINVAL) {
+ fail_errno("ksem_getvalue");
+ return;
+ }
+ pass();
+}
+TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
+
+static void
+post_invalid_semaphore(void)
+{
+
+ ksem_post_should_fail(STDERR_FILENO, EINVAL);
+}
+TEST(post_invalid_semaphore, "post of invalid semaphore");
+
+static void
+wait_invalid_semaphore(void)
+{
+
+ if (ksem_wait(STDERR_FILENO) >= 0) {
+ fail_err("ksem_wait() didn't fail");
+ return;
+ }
+ if (errno != EINVAL) {
+ fail_errno("ksem_wait");
+ return;
+ }
+ pass();
+}
+TEST(wait_invalid_semaphore, "wait for invalid semaphore");
+
+static void
+trywait_invalid_semaphore(void)
+{
+
+ if (ksem_trywait(STDERR_FILENO) >= 0) {
+ fail_err("ksem_trywait() didn't fail");
+ return;
+ }
+ if (errno != EINVAL) {
+ fail_errno("ksem_trywait");
+ return;
+ }
+ pass();
+}
+TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
+
+static void
+timedwait_invalid_semaphore(void)
+{
+
+ if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
+ fail_err("ksem_timedwait() didn't fail");
+ return;
+ }
+ if (errno != EINVAL) {
+ fail_errno("ksem_timedwait");
+ return;
+ }
+ pass();
+}
+TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
+
+static int
+checkvalue(semid_t id, int expected)
+{
+ int val;
+
+ if (ksem_getvalue(id, &val) < 0) {
+ fail_errno("ksem_getvalue");
+ return (-1);
+ }
+ if (val != expected) {
+ fail_err("sem value should be %d instead of %d", expected, val);
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+post_test(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+ if (checkvalue(id, 1) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (ksem_post(id) < 0) {
+ fail_errno("ksem_post");
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 2) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(post_test, "simple post");
+
+static void
+use_after_unlink_test(void)
+{
+ semid_t id;
+
+ /*
+ * Create named semaphore with value of 1 and then unlink it
+ * while still retaining the initial reference.
+ */
+ if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
+ fail_errno("ksem_open(O_CREAT | O_EXCL)");
+ return;
+ }
+ if (ksem_unlink(TEST_PATH) < 0) {
+ fail_errno("ksem_unlink");
+ ksem_close(id);
+ return;
+ }
+ if (checkvalue(id, 1) < 0) {
+ ksem_close(id);
+ return;
+ }
+
+ /* Post the semaphore to set its value to 2. */
+ if (ksem_post(id) < 0) {
+ fail_errno("ksem_post");
+ ksem_close(id);
+ return;
+ }
+ if (checkvalue(id, 2) < 0) {
+ ksem_close(id);
+ return;
+ }
+
+ /* Wait on the semaphore which should set its value to 1. */
+ if (ksem_wait(id) < 0) {
+ fail_errno("ksem_wait");
+ ksem_close(id);
+ return;
+ }
+ if (checkvalue(id, 1) < 0) {
+ ksem_close(id);
+ return;
+ }
+
+ if (ksem_close(id) < 0) {
+ fail_errno("ksem_close");
+ return;
+ }
+ pass();
+}
+TEST(use_after_unlink_test, "use named semaphore after unlink");
+
+static void
+unlocked_trywait(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /* This should succeed and decrement the value to 0. */
+ if (ksem_trywait(id) < 0) {
+ fail_errno("ksem_trywait()");
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(unlocked_trywait, "unlocked trywait");
+
+static void
+locked_trywait(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, 0) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /* This should fail with EAGAIN and leave the value at 0. */
+ if (ksem_trywait(id) >= 0) {
+ fail_err("ksem_trywait() didn't fail");
+ ksem_destroy(id);
+ return;
+ }
+ if (errno != EAGAIN) {
+ fail_errno("wrong error from ksem_trywait()");
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(locked_trywait, "locked trywait");
+
+/*
+ * Use a timer to post a specific semaphore after a timeout. A timer
+ * is scheduled via schedule_post(). check_alarm() must be called
+ * afterwards to clean up and check for errors.
+ */
+static semid_t alarm_id = -1;
+static int alarm_errno;
+static int alarm_handler_installed;
+
+static void
+alarm_handler(int signo)
+{
+
+ if (ksem_post(alarm_id) < 0)
+ alarm_errno = errno;
+}
+
+static int
+check_alarm(int just_clear)
+{
+ struct itimerval it;
+
+ bzero(&it, sizeof(it));
+ if (just_clear) {
+ setitimer(ITIMER_REAL, &it, NULL);
+ alarm_errno = 0;
+ alarm_id = -1;
+ return (0);
+ }
+ if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
+ fail_errno("setitimer");
+ return (-1);
+ }
+ if (alarm_errno != 0 && !just_clear) {
+ errno = alarm_errno;
+ fail_errno("ksem_post() (via timeout)");
+ alarm_errno = 0;
+ return (-1);
+ }
+ alarm_id = -1;
+
+ return (0);
+}
+
+static int
+schedule_post(semid_t id, u_int msec)
+{
+ struct itimerval it;
+
+ if (!alarm_handler_installed) {
+ if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
+ fail_errno("signal(SIGALRM)");
+ return (-1);
+ }
+ alarm_handler_installed = 1;
+ }
+ if (alarm_id != -1) {
+ fail_err("ksem_post() already scheduled");
+ return (-1);
+ }
+ alarm_id = id;
+ bzero(&it, sizeof(it));
+ it.it_value.tv_sec = msec / 1000;
+ it.it_value.tv_usec = (msec % 1000) * 1000;
+ if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
+ fail_errno("setitimer");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+timedwait(semid_t id, u_int msec, u_int *delta, int error)
+{
+ struct timespec start, end;
+
+ if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
+ fail_errno("clock_gettime(CLOCK_REALTIME)");
+ return (-1);
+ }
+ end.tv_sec = msec / 1000;
+ end.tv_nsec = msec % 1000 * 1000000;
+ timespecadd(&end, &start);
+ if (ksem_timedwait(id, &end) < 0) {
+ if (errno != error) {
+ fail_errno("ksem_timedwait");
+ return (-1);
+ }
+ } else if (error != 0) {
+ fail_err("ksem_timedwait() didn't fail");
+ return (-1);
+ }
+ if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
+ fail_errno("clock_gettime(CLOCK_REALTIME)");
+ return (-1);
+ }
+ timespecsub(&end, &start);
+ *delta = end.tv_nsec / 1000000;
+ *delta += end.tv_sec * 1000;
+ return (0);
+}
+
+static void
+unlocked_timedwait(void)
+{
+ semid_t id;
+ u_int elapsed;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /* This should succeed right away and set the value to 0. */
+ if (timedwait(id, 5000, &elapsed, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (!ELAPSED(elapsed, 0)) {
+ fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(unlocked_timedwait, "unlocked timedwait");
+
+static void
+expired_timedwait(void)
+{
+ semid_t id;
+ u_int elapsed;
+
+ if (ksem_init(&id, 0) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /* This should fail with a timeout and leave the value at 0. */
+ if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (!ELAPSED(elapsed, 2500)) {
+ fail_err(
+ "ksem_timedwait() of locked sem took %ums instead of 2500ms",
+ elapsed);
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(expired_timedwait, "locked timedwait timeout");
+
+static void
+locked_timedwait(void)
+{
+ semid_t id;
+ u_int elapsed;
+
+ if (ksem_init(&id, 0) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /*
+ * Schedule a post to trigger after 1000 ms. The subsequent
+ * timedwait should succeed after 1000 ms as a result w/o
+ * timing out.
+ */
+ if (schedule_post(id, 1000) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (timedwait(id, 2000, &elapsed, 0) < 0) {
+ check_alarm(1);
+ ksem_destroy(id);
+ return;
+ }
+ if (!ELAPSED(elapsed, 1000)) {
+ fail_err(
+ "ksem_timedwait() with delayed post took %ums instead of 1000ms",
+ elapsed);
+ check_alarm(1);
+ ksem_destroy(id);
+ return;
+ }
+ if (check_alarm(0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(locked_timedwait, "locked timedwait");
+
+static int
+testwait(semid_t id, u_int *delta)
+{
+ struct timespec start, end;
+
+ if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
+ fail_errno("clock_gettime(CLOCK_REALTIME)");
+ return (-1);
+ }
+ if (ksem_wait(id) < 0) {
+ fail_errno("ksem_wait");
+ return (-1);
+ }
+ if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
+ fail_errno("clock_gettime(CLOCK_REALTIME)");
+ return (-1);
+ }
+ timespecsub(&end, &start);
+ *delta = end.tv_nsec / 1000000;
+ *delta += end.tv_sec * 1000;
+ return (0);
+}
+
+static void
+unlocked_wait(void)
+{
+ semid_t id;
+ u_int elapsed;
+
+ if (ksem_init(&id, 1) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /* This should succeed right away and set the value to 0. */
+ if (testwait(id, &elapsed) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (!ELAPSED(elapsed, 0)) {
+ fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
+ ksem_destroy(id);
+ return;
+ }
+ if (checkvalue(id, 0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(unlocked_wait, "unlocked wait");
+
+static void
+locked_wait(void)
+{
+ semid_t id;
+ u_int elapsed;
+
+ if (ksem_init(&id, 0) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ /*
+ * Schedule a post to trigger after 1000 ms. The subsequent
+ * wait should succeed after 1000 ms as a result.
+ */
+ if (schedule_post(id, 1000) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+ if (testwait(id, &elapsed) < 0) {
+ check_alarm(1);
+ ksem_destroy(id);
+ return;
+ }
+ if (!ELAPSED(elapsed, 1000)) {
+ fail_err(
+ "ksem_wait() with delayed post took %ums instead of 1000ms",
+ elapsed);
+ check_alarm(1);
+ ksem_destroy(id);
+ return;
+ }
+ if (check_alarm(0) < 0) {
+ ksem_destroy(id);
+ return;
+ }
+
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(locked_wait, "locked wait");
+
+/*
+ * Fork off a child process. The child will open the semaphore via
+ * the same name. The child will then block on the semaphore waiting
+ * for the parent to post it.
+ */
+static int
+wait_twoproc_child(void *arg)
+{
+ semid_t id;
+
+ if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
+ return (CSTAT(1, errno));
+ if (ksem_wait(id) < 0)
+ return (CSTAT(2, errno));
+ if (ksem_close(id) < 0)
+ return (CSTAT(3, errno));
+ return (CSTAT(0, 0));
+}
+
+static void
+wait_twoproc_test(void)
+{
+ semid_t id;
+ int stat;
+
+ if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
+ fail_errno("ksem_open");
+ return;
+ }
+
+ if (schedule_post(id, 500) < 0) {
+ ksem_close(id);
+ ksem_unlink(TEST_PATH);
+ return;
+ }
+
+ if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
+ check_alarm(1);
+ ksem_close(id);
+ ksem_unlink(TEST_PATH);
+ return;
+ }
+
+ errno = CSTAT_ERROR(stat);
+ switch (CSTAT_CLASS(stat)) {
+ case 0:
+ pass();
+ break;
+ case 1:
+ fail_errno("child ksem_open()");
+ break;
+ case 2:
+ fail_errno("child ksem_wait()");
+ break;
+ case 3:
+ fail_errno("child ksem_close()");
+ break;
+ default:
+ fail_err("bad child state %#x", stat);
+ break;
+ }
+
+ check_alarm(1);
+ ksem_close(id);
+ ksem_unlink(TEST_PATH);
+}
+TEST(wait_twoproc_test, "two proc wait");
+
+static void
+maxvalue_test(void)
+{
+ semid_t id;
+ int val;
+
+ if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+ if (ksem_getvalue(id, &val) < 0) {
+ fail_errno("ksem_getvalue");
+ ksem_destroy(id);
+ return;
+ }
+ if (val != SEM_VALUE_MAX) {
+ fail_err("value %d != SEM_VALUE_MAX");
+ ksem_destroy(id);
+ return;
+ }
+ if (val < 0) {
+ fail_err("value < 0");
+ ksem_destroy(id);
+ return;
+ }
+ if (ksem_destroy(id) < 0) {
+ fail_errno("ksem_destroy");
+ return;
+ }
+ pass();
+}
+TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
+
+static void
+maxvalue_post_test(void)
+{
+ semid_t id;
+
+ if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
+ fail_errno("ksem_init");
+ return;
+ }
+
+ ksem_post_should_fail(id, EOVERFLOW);
+
+ ksem_destroy(id);
+}
+TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
+
+static void
+busy_destroy_test(void)
+{
+ char errbuf[_POSIX2_LINE_MAX];
+ struct kinfo_proc *kp;
+ semid_t id;
+ pid_t pid;
+ kvm_t *kd;
+ int count;
+
+ kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
+ if (kd == NULL) {
+ fail_err("kvm_openfiles: %s", errbuf);
+ return;
+ }
+
+ if (ksem_init(&id, 0) < 0) {
+ fail_errno("ksem_init");
+ kvm_close(kd);
+ return;
+ }
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ /* Error. */
+ fail_errno("fork");
+ ksem_destroy(id);
+ kvm_close(kd);
+ return;
+ case 0:
+ /* Child. */
+ ksem_wait(id);
+ exit(0);
+ }
+
+ /*
+ * Wait for the child process to block on the semaphore. This
+ * is a bit gross.
+ */
+ for (;;) {
+ kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
+ if (kp == NULL) {
+ fail_err("kvm_getprocs: %s", kvm_geterr(kd));
+ kvm_close(kd);
+ ksem_destroy(id);
+ return;
+ }
+ if (kp->ki_stat == SSLEEP &&
+ (strcmp(kp->ki_wmesg, "sem") == 0 ||
+ strcmp(kp->ki_wmesg, "ksem") == 0))
+ break;
+ usleep(1000);
+ }
+ kvm_close(kd);
+
+ ksem_destroy_should_fail(id, EBUSY);
+
+ /* Cleanup. */
+ ksem_post(id);
+ waitpid(pid, NULL, 0);
+ ksem_destroy(id);
+}
+TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
+
+static int
+exhaust_unnamed_child(void *arg)
+{
+ semid_t id;
+ int i, max;
+
+ max = (intptr_t)arg;
+ for (i = 0; i < max + 1; i++) {
+ if (ksem_init(&id, 1) < 0) {
+ if (errno == ENOSPC)
+ return (CSTAT(0, 0));
+ return (CSTAT(1, errno));
+ }
+ }
+ return (CSTAT(2, 0));
+}
+
+static void
+exhaust_unnamed_sems(void)
+{
+ size_t len;
+ int nsems_max, stat;
+
+ len = sizeof(nsems_max);
+ if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
+ 0) {
+ fail_errno("sysctl(p1003_1b.sem_nsems_max)");
+ return;
+ }
+
+ if (child_worker(exhaust_unnamed_child, (void *)nsems_max, &stat))
+ return;
+ errno = CSTAT_ERROR(stat);
+ switch (CSTAT_CLASS(stat)) {
+ case 0:
+ pass();
+ break;
+ case 1:
+ fail_errno("ksem_init");
+ break;
+ case 2:
+ fail_err("Limit of %d semaphores not enforced", nsems_max);
+ break;
+ default:
+ fail_err("bad child state %#x", stat);
+ break;
+ }
+}
+TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
+
+static int
+exhaust_named_child(void *arg)
+{
+ char buffer[64];
+ semid_t id;
+ int i, max;
+
+ max = (intptr_t)arg;
+ for (i = 0; i < max + 1; i++) {
+ snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
+ if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
+ if (errno == ENOSPC || errno == EMFILE ||
+ errno == ENFILE)
+ return (CSTAT(0, 0));
+ return (CSTAT(1, errno));
+ }
+ }
+ return (CSTAT(2, errno));
+}
+
+static void
+exhaust_named_sems(void)
+{
+ char buffer[64];
+ size_t len;
+ int i, nsems_max, stat;
+
+ len = sizeof(nsems_max);
+ if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
+ 0) {
+ fail_errno("sysctl(p1003_1b.sem_nsems_max)");
+ return;
+ }
+
+ if (child_worker(exhaust_named_child, (void *)nsems_max, &stat) < 0)
+ return;
+ errno = CSTAT_ERROR(stat);
+ switch (CSTAT_CLASS(stat)) {
+ case 0:
+ pass();
+ break;
+ case 1:
+ fail_errno("ksem_open");
+ break;
+ case 2:
+ fail_err("Limit of %d semaphores not enforced", nsems_max);
+ break;
+ default:
+ fail_err("bad child state %#x", stat);
+ break;
+ }
+
+ /* Cleanup any semaphores created by the child. */
+ for (i = 0; i < nsems_max + 1; i++) {
+ snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
+ ksem_unlink(buffer);
+ }
+}
+TEST(exhaust_named_sems, "exhaust named semaphores (1)");
+
+static int
+fdlimit_set(void *arg)
+{
+ struct rlimit rlim;
+ int max;
+
+ max = (intptr_t)arg;
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ return (CSTAT(3, errno));
+ rlim.rlim_cur = max;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ return (CSTAT(4, errno));
+ return (0);
+}
+
+static int
+fdlimit_unnamed_child(void *arg)
+{
+ int stat;
+
+ stat = fdlimit_set(arg);
+ if (stat == 0)
+ stat = exhaust_unnamed_child(arg);
+ return (stat);
+}
+
+static void
+fdlimit_unnamed_sems(void)
+{
+ int nsems_max, stat;
+
+ nsems_max = 10;
+ if (child_worker(fdlimit_unnamed_child, (void *)nsems_max, &stat))
+ return;
+ errno = CSTAT_ERROR(stat);
+ switch (CSTAT_CLASS(stat)) {
+ case 0:
+ pass();
+ break;
+ case 1:
+ fail_errno("ksem_init");
+ break;
+ case 2:
+ fail_err("Limit of %d semaphores not enforced", nsems_max);
+ break;
+ case 3:
+ fail_errno("getrlimit");
+ break;
+ case 4:
+ fail_errno("getrlimit");
+ break;
+ default:
+ fail_err("bad child state %#x", stat);
+ break;
+ }
+}
+TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
+
+static int
+fdlimit_named_child(void *arg)
+{
+ int stat;
+
+ stat = fdlimit_set(arg);
+ if (stat == 0)
+ stat = exhaust_named_child(arg);
+ return (stat);
+}
+
+static void
+fdlimit_named_sems(void)
+{
+ char buffer[64];
+ int i, nsems_max, stat;
+
+ nsems_max = 10;
+ if (child_worker(fdlimit_named_child, (void *)nsems_max, &stat) < 0)
+ return;
+ errno = CSTAT_ERROR(stat);
+ switch (CSTAT_CLASS(stat)) {
+ case 0:
+ pass();
+ break;
+ case 1:
+ fail_errno("ksem_open");
+ break;
+ case 2:
+ fail_err("Limit of %d semaphores not enforced", nsems_max);
+ break;
+ case 3:
+ fail_errno("getrlimit");
+ break;
+ case 4:
+ fail_errno("getrlimit");
+ break;
+ default:
+ fail_err("bad child state %#x", stat);
+ break;
+ }
+
+ /* Cleanup any semaphores created by the child. */
+ for (i = 0; i < nsems_max + 1; i++) {
+ snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
+ ksem_unlink(buffer);
+ }
+}
+TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
+
+int
+main(int argc, char *argv[])
+{
+
+ signal(SIGSYS, SIG_IGN);
+ run_tests();
+ return (0);
+}
diff --git a/tools/regression/posixsem/posixsem.t b/tools/regression/posixsem/posixsem.t
new file mode 100644
index 0000000..198d2be
--- /dev/null
+++ b/tools/regression/posixsem/posixsem.t
@@ -0,0 +1,5 @@
+#!/bin/sh
+#
+# $FreeBSD$
+
+./posixsem
diff --git a/tools/regression/posixsem/test.c b/tools/regression/posixsem/test.c
new file mode 100644
index 0000000..8583a0d
--- /dev/null
+++ b/tools/regression/posixsem/test.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2008 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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>
+__FBSDID("$FreeBSD$");
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "test.h"
+
+static int test_index;
+static struct regression_test *test;
+static int test_acknowleged;
+
+SET_DECLARE(regression_tests_set, struct regression_test);
+
+/*
+ * Outputs a test summary of the following:
+ *
+ * <status> <test #> [name] [# <fmt> [fmt args]]
+ */
+static void
+vprint_status(const char *status, const char *fmt, va_list ap)
+{
+
+ printf("%s %d", status, test_index);
+ if (test->rt_name)
+ printf(" - %s", test->rt_name);
+ if (fmt) {
+ printf(" # ");
+ vprintf(fmt, ap);
+ }
+ printf("\n");
+ test_acknowleged = 1;
+}
+
+static void
+print_status(const char *status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprint_status(status, fmt, ap);
+ va_end(ap);
+}
+
+void
+pass(void)
+{
+
+ print_status("ok", NULL);
+}
+
+void
+fail(void)
+{
+
+ print_status("not ok", NULL);
+}
+
+void
+fail_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprint_status("not ok", fmt, ap);
+ va_end(ap);
+}
+
+void
+skip(const char *reason)
+{
+
+ print_status("ok", "skip %s", reason);
+}
+
+void
+todo(const char *reason)
+{
+
+ print_status("not ok", "TODO %s", reason);
+}
+
+void
+run_tests(void)
+{
+ struct regression_test **testp;
+
+ printf("1..%td\n", SET_COUNT(regression_tests_set));
+ test_index = 1;
+ SET_FOREACH(testp, regression_tests_set) {
+ test_acknowleged = 0;
+ test = *testp;
+ test->rt_function();
+ if (!test_acknowleged)
+ print_status("not ok", "unknown status");
+ test_index++;
+ }
+}
diff --git a/tools/regression/posixsem/test.h b/tools/regression/posixsem/test.h
new file mode 100644
index 0000000..505679b
--- /dev/null
+++ b/tools/regression/posixsem/test.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2008 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __TEST_H__
+#define __TEST_H__
+
+#include <sys/linker_set.h>
+
+struct regression_test {
+ void (*rt_function)(void);
+ const char *rt_name;
+};
+
+#define TEST(function, name) \
+ static struct regression_test _regtest_##function = { \
+ (function), \
+ (name) \
+ }; \
+ DATA_SET(regression_tests_set, _regtest_##function)
+
+void fail(void);
+void fail_err(const char *fmt, ...);
+void pass(void);
+void run_tests(void);
+void skip(const char *reason);
+void todo(const char *reason);
+
+#define fail_errno(tag) fail_err("%s: %s", (tag), strerror(errno))
+
+#endif /* !__TEST_H__ */
diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c
index 950b70d..39ce82b 100644
--- a/usr.bin/procstat/procstat_files.c
+++ b/usr.bin/procstat/procstat_files.c
@@ -222,6 +222,10 @@ procstat_files(pid_t pid, struct kinfo_proc *kipp)
str = "h";
break;
+ case KF_TYPE_SEM:
+ str = "e";
+ break;
+
case KF_TYPE_NONE:
case KF_TYPE_UNKNOWN:
default:
OpenPOWER on IntegriCloud