diff options
-rw-r--r-- | sys/kern/kern_descrip.c | 6 | ||||
-rw-r--r-- | sys/kern/uipc_sem.c | 1186 | ||||
-rw-r--r-- | sys/kern/uipc_shm.c | 4 | ||||
-rw-r--r-- | sys/modules/sem/Makefile | 2 | ||||
-rw-r--r-- | sys/security/mac/mac_framework.h | 11 | ||||
-rw-r--r-- | sys/security/mac/mac_policy.h | 19 | ||||
-rw-r--r-- | sys/security/mac/mac_posix_sem.c | 30 | ||||
-rw-r--r-- | sys/security/mac_biba/mac_biba.c | 31 | ||||
-rw-r--r-- | sys/security/mac_mls/mac_mls.c | 31 | ||||
-rw-r--r-- | sys/security/mac_stub/mac_stub.c | 21 | ||||
-rw-r--r-- | sys/security/mac_test/mac_test.c | 35 | ||||
-rw-r--r-- | sys/sys/file.h | 1 | ||||
-rw-r--r-- | sys/sys/ksem.h | 42 | ||||
-rw-r--r-- | sys/sys/user.h | 1 | ||||
-rw-r--r-- | tools/regression/posixsem/Makefile | 11 | ||||
-rw-r--r-- | tools/regression/posixsem/posixsem.c | 1437 | ||||
-rw-r--r-- | tools/regression/posixsem/posixsem.t | 5 | ||||
-rw-r--r-- | tools/regression/posixsem/test.c | 128 | ||||
-rw-r--r-- | tools/regression/posixsem/test.h | 59 | ||||
-rw-r--r-- | usr.bin/procstat/procstat_files.c | 4 |
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: |