--- //depot/projects/trustedbsd/base/sys/kern/uipc_sem.c	2003/06/24 12:28:09
+++ //depot/projects/trustedbsd/mac/sys/kern/uipc_sem.c	2003/10/29 21:23:38
@@ -1,7 +1,13 @@
 /*
  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
  * All rights reserved.
  *
+ * This software was developed for the FreeBSD Project in part by Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
+ * as part of the DARPA CHATS research program.
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -28,6 +34,7 @@
 __FBSDID("$FreeBSD: src/sys/kern/uipc_sem.c,v 1.9 2003/06/11 00:56:58 obrien Exp $");
 
 #include "opt_posix.h"
+#include "opt_mac.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -47,6 +54,11 @@
 #include <sys/malloc.h>
 #include <sys/jail.h>
 #include <sys/fcntl.h>
+#ifdef MAC
+#include <sys/_label.h>
+#include <sys/mac.h>
+#include <posix4/ksem.h>
+#endif
 
 #include <posix4/posix4.h>
 #include <posix4/semaphore.h>
@@ -54,8 +66,8 @@
 
 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);
+struct ksem **ksret, mode_t mode, unsigned int value);
+static void sem_free(struct ksem *ksnew, int checkrefs);
 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);
@@ -69,6 +81,8 @@
     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 ksem_open_existing(struct thread *td, struct ksem *ks, int dir, 
+    semid_t *idpu, semid_t *idpk);
 static int kern_sem_unlink(struct thread *td, const char *name);
 
 #ifndef SEM_MAX
@@ -79,7 +93,18 @@
 
 #define SEM_TO_ID(x)	((intptr_t)(x))
 #define ID_TO_SEM(x)	id_to_sem(x)
+#define SEM_FREE(ks) sem_free(ks, 1)
+#define SEM_DROP(ks) sem_free(ks, 0)
+#define REF_UP(ks) sem_ref(ks)
+#define REF_DOWN(ksem)    \
+		do { \
+		    sem_rel((ksem)); /* Pump down */ \
+		    if((ksem)->ks_unlinked &&  \
+			LIST_EMPTY(&(ksem)->ks_users)) \
+			    SEM_FREE((ksem)); \
+		} while (0)
 
+#ifndef MAC
 struct kuser {
 	pid_t ku_pid;
 	LIST_ENTRY(kuser) ku_next;
@@ -97,17 +122,19 @@
 	struct cv ks_cv;		/* waiters sleep here */
 	int ks_waiters;			/* number of waiters */
 	LIST_HEAD(, kuser) ks_users;	/* pids using this sem */
+	struct mtx ks_mtx;        	/* mutex protecting this semaphore */
+	int ks_unlinked;		/* Whether the named sem is unlinked */
 };
+#else
+struct kuser;
+struct ksem;
+#endif
 
 /*
  * 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);
-/*
- * semaphores still in use but have been sem_unlink()'d go here.
- */
-LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
 
 static struct mtx sem_lock;
 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
@@ -118,6 +145,9 @@
 
 static eventhandler_tag sem_exit_tag, sem_exec_tag;
 
+#ifndef SEM_DEBUG
+#define SEM_DEBUG
+#endif
 #ifdef SEM_DEBUG
 #define DP(x)	printf x
 #else
@@ -128,7 +158,7 @@
 void
 sem_ref(struct ksem *ks)
 {
-
+	mtx_assert(&sem_lock, MA_OWNED);
 	ks->ks_ref++;
 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
 }
@@ -138,9 +168,9 @@
 sem_rel(struct ksem *ks)
 {
 
+	mtx_assert(&sem_lock, MA_OWNED);
+	ks->ks_ref--;
 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
-	if (--ks->ks_ref == 0)
-		sem_free(ks);
 }
 
 static __inline struct ksem *id_to_sem(semid_t id);
@@ -152,6 +182,7 @@
 {
 	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));
@@ -167,12 +198,15 @@
 {
 	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);
 }
 
+/* Used by both sem_init and sem_open to create a new semaphore. */
+
 static int
 sem_create(td, name, ksret, mode, value)
 	struct thread *td;
@@ -185,11 +219,11 @@
 	struct proc *p;
 	struct ucred *uc;
 	size_t len;
-	int error;
 
 	DP(("sem_create\n"));
 	p = td->td_proc;
 	uc = td->td_ucred;
+	/* XXX Use p31b_getcfg(CTL_P1003_1B_SEM_VALUE_MAX) instead? */
 	if (value > SEM_VALUE_MAX)
 		return (EINVAL);
 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
@@ -211,27 +245,34 @@
 	}
 	ret->ks_mode = mode;
 	ret->ks_value = value;
-	ret->ks_ref = 1;
+	ret->ks_ref = 0;
 	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);
+	mtx_init(&ret->ks_mtx, "ks_mtx", "ks_mtx", MTX_DEF);
 	if (name != NULL)
-		sem_enter(td->td_proc, ret);
-	*ksret = ret;
+		sem_enter(td->td_proc, ret); /* This invokes sem_ref */
+	else
+		ret->ks_ref = 1;
+#ifdef MAC
+	mac_init_posix_ksem(ret);
+	mac_create_posix_ksem(uc, ret);
+#endif
 	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 {
-		nsems++;
-		error = 0;
-	}
+		if (name != NULL) 
+		    sem_leave(td->td_proc, ret); /* This invokes sem_rel */
+		SEM_DROP(ret); /* sem_free does a nsem-- */
+		mtx_unlock(&sem_lock);
+		return (ENFILE);
+	} 
 	mtx_unlock(&sem_lock);
-	return (error);
+	*ksret = ret;
+	return (0);
 }
 
 #ifndef _SYS_SYSPROTO_H_
@@ -263,15 +304,14 @@
 	semid_t id;
 	int error;
 
-	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
-	if (error)
+	if((error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value)))
 		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);
+			SEM_DROP(ks);
 			mtx_unlock(&sem_lock);
 			return (error);
 		}
@@ -302,10 +342,9 @@
 {
 	char name[SEM_MAX_NAMELEN + 1];
 	size_t done;
-	int error;
+	int error = 0;
 
-	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
-	if (error)
+	if ((error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done)))
 		return (error);
 	DP((">>> sem_open start\n"));
 	error = kern_sem_open(td, UIO_USERSPACE,
@@ -315,6 +354,52 @@
 }
 
 static int
+ksem_open_existing(struct thread *td, struct ksem *ks, int dir, semid_t *idpu, 
+	semid_t *idpk)
+{
+	int error = 0;
+	mtx_assert(&sem_lock,MA_OWNED);
+	mtx_assert(&ks->ks_mtx,MA_NOTOWNED);
+	if((error = sem_perm(td, ks))) {
+		mtx_unlock(&sem_lock);
+		return (error);
+	}
+	/* 
+	 * If already queued up for unlinking. 
+	 * Then according to spec cant let reconnect to this semaphore.
+	 */
+	if(ks->ks_unlinked) {
+		mtx_unlock(&sem_lock);
+		return (EPERM);
+	}
+	*idpk = SEM_TO_ID(ks);
+	REF_UP(ks); /* Pump up the refs to avoid the race with SEM_FREE */
+	mtx_unlock(&sem_lock);
+#ifdef MAC
+	mtx_lock(&ks->ks_mtx);
+	if((error = mac_check_posix_sem_openexisting(td->td_ucred, ks))) {
+	    DP(("MAC Framework: mac_check_posix_sem_openexisting access denied\n"));
+	    mtx_unlock(&ks->ks_mtx);
+	    goto err_open_existing;
+	}
+	mtx_unlock(&ks->ks_mtx);
+#endif
+	if (dir == UIO_USERSPACE) {
+		if ((error = copyout(idpk, idpu, sizeof(*idpk)))) {
+			goto err_open_existing;
+		}
+	} else {
+		*idpu = *idpk;
+	}
+	sem_enter(td->td_proc, ks);
+err_open_existing:
+	mtx_lock(&sem_lock);
+	REF_DOWN(ks);
+	mtx_unlock(&sem_lock);
+	return(error);
+}
+
+static int
 kern_sem_open(td, dir, name, oflag, mode, value, idp)
 	struct thread *td;
 	int dir;
@@ -325,7 +410,7 @@
 	semid_t *idp;
 {
 	struct ksem *ksnew, *ks;
-	int error;
+	int error = 0;
 	semid_t id;
 
 	ksnew = NULL;
@@ -353,8 +438,7 @@
 		 * We may block during creation, so drop the lock.
 		 */
 		mtx_unlock(&sem_lock);
-		error = sem_create(td, name, &ksnew, mode, value);
-		if (error != 0)
+		if((error = sem_create(td, name, &ksnew, mode, value)))
 			return (error);
 		id = SEM_TO_ID(ksnew);
 		if (dir == UIO_USERSPACE) {
@@ -363,7 +447,7 @@
 			if (error) {
 				mtx_lock(&sem_lock);
 				sem_leave(td->td_proc, ksnew);
-				sem_rel(ksnew);
+				SEM_DROP(ksnew);
 				mtx_unlock(&sem_lock);
 				return (error);
 			}
@@ -380,46 +464,28 @@
 		if (ks != NULL) {
 			/* we lost... */
 			sem_leave(td->td_proc, ksnew);
-			sem_rel(ksnew);
+			SEM_DROP(ksnew);
 			/* we lost and we can't loose... */
 			if ((oflag & O_EXCL) != 0) {
 				mtx_unlock(&sem_lock);
 				return (EEXIST);
 			}
+			/* Use the sem created by the winner */
+			else {
+			    /* ksem_open_existing unlocks sem_lock */
+			    error = ksem_open_existing(td, ks, dir, idp, &id);
+			}
 		} 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"));
+			mtx_unlock(&sem_lock);
 		}
-		mtx_unlock(&sem_lock);
 	} else {
-		/*
-		 * if we aren't the creator, then enforce permissions.
-		 */
-		error = sem_perm(td, ks);
-		if (!error)
-			sem_ref(ks);
-		mtx_unlock(&sem_lock);
-		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;
-		}
-		sem_enter(td->td_proc, ks);
-		mtx_lock(&sem_lock);
-		sem_rel(ks);
-		mtx_unlock(&sem_lock);
+		/* ksem_open_existing unlocks sem_lock */
+		error = ksem_open_existing(td, ks, dir, idp, &id);
 	}
 	return (error);
 }
@@ -443,18 +509,27 @@
 }
 
 static void
-sem_free(struct ksem *ks)
+sem_free(struct ksem *ks, int checkrefs)
 {
-
+	mtx_assert(&sem_lock, MA_OWNED);
+	mtx_assert(&ks->ks_mtx, MA_NOTOWNED);
+        if(checkrefs && (ks->ks_ref > 0)) 
+		return;
 	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);
+#ifdef MAC
+	mac_destroy_posix_ksem(ks);
+#endif
+	mtx_destroy(&ks->ks_mtx);
 	free(ks, M_SEM);
 }
 
+
 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
 
 static __inline struct kuser *
@@ -463,7 +538,8 @@
 	struct ksem *ks;
 {
 	struct kuser *k;
-
+	
+	mtx_assert(&sem_lock, MA_OWNED);
 	LIST_FOREACH(k, &ks->ks_users, ku_next)
 		if (k->ku_pid == p->p_pid)
 			return (k);
@@ -475,9 +551,15 @@
 	struct thread *td;
 	struct ksem *ks;
 {
+	struct kuser *k;
+	int ret = 0;
+
+	mtx_assert(&sem_lock, MA_OWNED);
+	k = sem_getuser(td->td_proc, ks);
+	if ((ks->ks_name == NULL && sem_perm(td, ks)) ||  k != NULL)
+		ret = 1;
+	return ret;
 	
-	return ((ks->ks_name == NULL && sem_perm(td, ks))
-	    || sem_getuser(td->td_proc, ks) != NULL);
 }
 
 static int
@@ -485,20 +567,21 @@
 	struct proc *p;
 	struct ksem *ks;
 {
-	struct kuser *k;
+	struct kuser *k=NULL;
 
 	DP(("sem_leave: ks = %p\n", ks));
+        mtx_assert(&sem_lock, MA_OWNED);
+	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
 	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);
+	if (k == NULL) {
+		return (EINVAL);
 	}
-	return (EINVAL);
+	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);
 }
 
 static void
@@ -508,7 +591,9 @@
 {
 	struct kuser *ku, *k;
 
-	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
+	mtx_assert(&sem_lock, MA_NOTOWNED);
+	mtx_assert(&ks->ks_mtx, MA_NOTOWNED);
+	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK | M_ZERO);
 	ku->ku_pid = p->p_pid;
 	mtx_lock(&sem_lock);
 	k = sem_getuser(p, ks);
@@ -549,20 +634,28 @@
 	const char *name;
 {
 	struct ksem *ks;
-	int error;
+	int error = 0;
 
 	mtx_lock(&sem_lock);
 	ks = sem_lookup_byname(name);
-	if (ks == NULL)
+	if (ks == NULL) {
 		error = ENOENT;
-	else
-		error = sem_perm(td, ks);
+		goto err_unlink;
+	} 
+	if ((error = sem_perm(td, ks)))
+		goto err_unlink;
+#ifdef MAC
+	if((error = mac_check_posix_sem_unlink(td->td_ucred, ks))) {
+		DP(("MAC Framework: mac_check_posix_sem_unlink access \
+				denied\n"));
+		goto err_unlink;
+	}
+#endif
 	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);
-	}
+	ks->ks_unlinked = 1;
+	if(LIST_EMPTY(&ks->ks_users))
+		SEM_FREE(ks);
+err_unlink:
 	mtx_unlock(&sem_lock);
 	return (error);
 }
@@ -589,12 +682,24 @@
 	struct ksem *ks;
 	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);
+	error = EINVAL;
+	if (ks != NULL && ks->ks_name != NULL) {
+#ifdef MAC
+		if ((error = mac_check_posix_sem_close(td->td_ucred, ks))) {
+			DP(("MAC Framework: mac_check_posix_sem_close access \
+						denied\n"));
+			goto err_close;
+		}
+#endif
+		if ((error = sem_leave(td->td_proc, ks)))
+			goto err_close;
+		if (ks->ks_unlinked && LIST_EMPTY(&ks->ks_users))
+			SEM_FREE(ks);
+	}
+err_close:
 	mtx_unlock(&sem_lock);
 	return (error);
 }
@@ -620,23 +725,35 @@
 	semid_t id;
 {
 	struct ksem *ks;
-	int error;
+	int error = 0;
 
 	mtx_lock(&sem_lock);
 	ks = ID_TO_SEM(id);
 	if (ks == NULL || !sem_hasopen(td, ks)) {
-		error = EINVAL;
-		goto err;
+		mtx_unlock(&sem_lock);
+		return (EINVAL);
 	}
+	REF_UP(ks);/* Pump up the refs to avoid the race with SEM_FREE */
+	mtx_unlock(&sem_lock);
+
+	mtx_lock(&ks->ks_mtx);
 	if (ks->ks_value == SEM_VALUE_MAX) {
 		error = EOVERFLOW;
-		goto err;
+		goto err_post;
+	}
+#ifdef MAC
+	if ((error = mac_check_posix_sem_post(td->td_ucred, ks))) {
+		DP(("MAC Framework: mac_check_posix_sem_post access denied\n"));
+		goto err_post;
 	}
+#endif
 	++ks->ks_value;
 	if (ks->ks_waiters > 0)
 		cv_signal(&ks->ks_cv);
-	error = 0;
-err:
+err_post:
+	mtx_unlock(&ks->ks_mtx);
+	mtx_lock(&sem_lock);
+	REF_DOWN(ks);
 	mtx_unlock(&sem_lock);
 	return (error);
 }
@@ -679,37 +796,46 @@
 	int tryflag;
 {
 	struct ksem *ks;
-	int error;
+	int error = 0;
 
 	DP((">>> kern_sem_wait entered!\n"));
 	mtx_lock(&sem_lock);
 	ks = ID_TO_SEM(id);
 	if (ks == NULL) {
 		DP(("kern_sem_wait ks == NULL\n"));
-		error = EINVAL;
-		goto err;
+		mtx_unlock(&sem_lock);
+		return (EINVAL);
 	}
-	sem_ref(ks);
 	if (!sem_hasopen(td, ks)) {
 		DP(("kern_sem_wait hasopen failed\n"));
-		error = EINVAL;
-		goto err;
+		mtx_unlock(&sem_lock);
+		return (EINVAL);
+	}
+	REF_UP(ks);/* Pump up the refs to avoid the race with SEM_FREE */
+	mtx_unlock(&sem_lock);
+
+	mtx_lock(&ks->ks_mtx);
+#ifdef MAC
+	if ((error = mac_check_posix_sem_wait(td->td_ucred, ks))) {
+		DP(("MAC Framework: mac_check_posix_sem_wait access denied\n"));
+		goto err_wait;
 	}
+#endif
 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
 	if (ks->ks_value == 0) {
 		ks->ks_waiters++;
-		error = tryflag ? EAGAIN : cv_wait_sig(&ks->ks_cv, &sem_lock);
+		error = tryflag ? EAGAIN : cv_wait_sig(&ks->ks_cv, &ks->ks_mtx);
 		ks->ks_waiters--;
 		if (error)
-			goto err;
+			goto err_wait;
 	}
 	ks->ks_value--;
-	error = 0;
-err:
-	if (ks != NULL)
-		sem_rel(ks);
+err_wait:
+	mtx_unlock(&ks->ks_mtx);
+	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
+	mtx_lock(&sem_lock);
+	REF_DOWN(ks);
 	mtx_unlock(&sem_lock);
-	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
 	return (error);
 }
 
@@ -734,9 +860,24 @@
 		mtx_unlock(&sem_lock);
 		return (EINVAL);
 	}
+	REF_UP(ks);/* Pump up the refs to avoid the race with SEM_FREE */
+	mtx_unlock(&sem_lock);
+
+	mtx_lock(&ks->ks_mtx);
+#ifdef MAC
+        if((error = mac_check_posix_sem_getvalue(td->td_ucred, ks))) {
+                DP(("MAC Framework: mac_check_posix_sem_getvalue access denied\n"));            
+                mtx_unlock(&ks->ks_mtx);
+                goto err_getvalue;
+        }
+#endif
 	val = ks->ks_value;
+	mtx_unlock(&ks->ks_mtx);
+	error = copyout(&val, uap->val, sizeof(val));
+err_getvalue:
+	mtx_lock(&sem_lock);
+	REF_DOWN(ks);
 	mtx_unlock(&sem_lock);
-	error = copyout(&val, uap->val, sizeof(val));
 	return (error);
 }
 
@@ -752,22 +893,24 @@
 	struct ksem_destroy_args *uap;
 {
 	struct ksem *ks;
-	int error;
+	int error = 0;
 
 	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;
+		goto err_destroy;
 	}
-	if (ks->ks_waiters != 0) {
-		error = EBUSY;
-		goto err;
+#ifdef MAC
+	if((error = mac_check_posix_sem_destroy(td->td_ucred, ks))) {
+		DP(("MAC Framework: mac_check_posix_sem_destroy access denied\n"));
+		goto err_destroy;
 	}
-	sem_rel(ks);
-	error = 0;
-err:
+#endif
+	ks->ks_unlinked = 1; /* Indicate that the sem needs to be destroyed */
+	SEM_FREE(ks);
+err_destroy:
 	mtx_unlock(&sem_lock);
 	return (error);
 }
@@ -783,13 +926,9 @@
 	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);
+		if((ks->ks_name != NULL) && (!sem_leave(p, ks)))
+		    if (ks->ks_unlinked && LIST_EMPTY(&ks->ks_users))
+			SEM_FREE(ks);
 		ks = ksnext;
 	}
 	mtx_unlock(&sem_lock);
