From: James Morris 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. Signed-off-by: James Morris Signed-off-by: Stephen Smalley Signed-off-by: Andrew Morton --- 25-akpm/security/selinux/avc.c | 25 +++++++++++++++++-------- 1 files changed, 17 insertions(+), 8 deletions(-) diff -puN security/selinux/avc.c~selinux-scalability-convert-avc-to-rcu-fix security/selinux/avc.c --- 25/security/selinux/avc.c~selinux-scalability-convert-avc-to-rcu-fix 2004-12-11 01:51:32.032502296 -0800 +++ 25-akpm/security/selinux/avc.c 2004-12-11 01:51:32.036501688 -0800 @@ -254,6 +254,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); @@ -732,6 +739,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); @@ -747,17 +760,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); @@ -783,9 +792,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; } _