[PATCH] SELinux - fix bug in avc_update_node()

From: James Morris
Date: Fri Dec 10 2004 - 22:02:00 EST


The patch below fixes a bug in avc_update_node(), reported by Alan Cox.
This can only happen on a UP kernel (where spin_trylock() always succeeds)
and when SELinux is running in non-enforcing mode. Under these
circumstances, the code can cause an avc node to be reclaimed as it is
updating it, leading to an oops.

The patch below fixes this by ensuring that avc node allocation and
possible reclamation happens before the cache lookup.

Please apply.

Signed-off-by: James Morris <jmorris@xxxxxxxxxx>
Signed-off-by: Stephen Smalley <sds@xxxxxxxxxxxxxx>

---

security/selinux/avc.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)

diff -purN -X dontdiff linux-2.6.10-rc2-mm4.o/security/selinux/avc.c linux-2.6.10-rc2-mm4.w/security/selinux/avc.c
--- linux-2.6.10-rc2-mm4.o/security/selinux/avc.c 2004-12-09 16:25:19.000000000 -0500
+++ linux-2.6.10-rc2-mm4.w/security/selinux/avc.c 2004-12-10 11:26:20.903442608 -0500
@@ -249,6 +249,13 @@ static void avc_node_delete(struct avc_n
atomic_dec(&avc_cache.active_nodes);
}

+static void avc_node_kill(struct avc_node *node)
+{
+ kmem_cache_free(avc_node_cachep, node);
+ avc_cache_stats_incr(frees);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
static void avc_node_replace(struct avc_node *new, struct avc_node *old)
{
list_replace_rcu(&old->list, &new->list);
@@ -722,6 +729,12 @@ static int avc_update_node(u32 event, u3
unsigned long flag;
struct avc_node *pos, *node, *orig = NULL;

+ node = avc_alloc_node();
+ if (!node) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
/* Lock the target slot */
hvalue = avc_hash(ssid, tsid, tclass);
spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
@@ -737,17 +750,13 @@ static int avc_update_node(u32 event, u3

if (!orig) {
rc = -ENOENT;
- goto out;
+ avc_node_kill(node);
+ goto out_unlock;
}

/*
* Copy and replace original node.
*/
- node = avc_alloc_node();
- if (!node) {
- rc = -ENOMEM;
- goto out;
- }

avc_node_populate(node, ssid, tsid, tclass, &orig->ae);

@@ -773,9 +782,9 @@ static int avc_update_node(u32 event, u3
break;
}
avc_node_replace(node, orig);
-out:
+out_unlock:
spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
-
+out:
return rc;
}


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/