
From: James Morris <jmorris@redhat.com>

This patch by Kaigai Kohei fixes a bug in the SELinux sidtab code, where we
do a spin_unlock_irq() while nested under another irq lock, which enables
interrupts and allows a deadlock to happen:

  sidtab_set() is called between POLICY_WRLOCK and POLICY_WRUNLOCK in
  services.c:1092.  sidtab_set() uses SIDTAB_LOCK()/SIDTAB_UNLOCK(), but
  SIDTAB_UNLOCK() enables any interruptions because it's defined as
  spin_unlock_irq().  If an interruption occurs between SIDTAB_UNLOCK() and
  POLICY_WRUNLOCK, and interruption context try to hold the POLICY_RDLOCK,
  then a deadlock happen in the result.

The solution is to save & restore flags on the inner lock, per the patch
below.

Signed-off-by: James Morris <jmorris@redhat.com>
Signed-off-by: Stephen Smalley <sds@epoch.ncsc.mil>
Signed-off-by: Kaigai Kohei <kaigai@ak.jp.nec.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/security/selinux/ss/sidtab.c |   21 +++++++++++++--------
 1 files changed, 13 insertions(+), 8 deletions(-)

diff -puN security/selinux/ss/sidtab.c~selinux-fix-sidtab-locking-bug security/selinux/ss/sidtab.c
--- 25/security/selinux/ss/sidtab.c~selinux-fix-sidtab-locking-bug	2004-10-26 19:05:21.593190384 -0700
+++ 25-akpm/security/selinux/ss/sidtab.c	2004-10-26 19:05:21.597189776 -0700
@@ -16,8 +16,8 @@
 (sid & SIDTAB_HASH_MASK)
 
 #define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
-#define SIDTAB_LOCK(s) spin_lock_irq(&s->lock)
-#define SIDTAB_UNLOCK(s) spin_unlock_irq(&s->lock)
+#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
+#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
 
 int sidtab_init(struct sidtab *s)
 {
@@ -237,12 +237,13 @@ int sidtab_context_to_sid(struct sidtab 
 {
 	u32 sid;
 	int ret = 0;
+	unsigned long flags;
 
 	*out_sid = SECSID_NULL;
 
 	sid = sidtab_search_context(s, context);
 	if (!sid) {
-		SIDTAB_LOCK(s);
+		SIDTAB_LOCK(s, flags);
 		/* Rescan now that we hold the lock. */
 		sid = sidtab_search_context(s, context);
 		if (sid)
@@ -257,7 +258,7 @@ int sidtab_context_to_sid(struct sidtab 
 		if (ret)
 			s->next_sid--;
 unlock_out:
-		SIDTAB_UNLOCK(s);
+		SIDTAB_UNLOCK(s, flags);
 	}
 
 	if (ret)
@@ -320,17 +321,21 @@ void sidtab_destroy(struct sidtab *s)
 
 void sidtab_set(struct sidtab *dst, struct sidtab *src)
 {
-	SIDTAB_LOCK(src);
+	unsigned long flags;
+
+	SIDTAB_LOCK(src, flags);
 	dst->htable = src->htable;
 	dst->nel = src->nel;
 	dst->next_sid = src->next_sid;
 	dst->shutdown = 0;
-	SIDTAB_UNLOCK(src);
+	SIDTAB_UNLOCK(src, flags);
 }
 
 void sidtab_shutdown(struct sidtab *s)
 {
-	SIDTAB_LOCK(s);
+	unsigned long flags;
+
+	SIDTAB_LOCK(s, flags);
 	s->shutdown = 1;
-	SIDTAB_UNLOCK(s);
+	SIDTAB_UNLOCK(s, flags);
 }
_
