Skip to content

Commit

Permalink
apparmor: fix IRQ stack overflow during free_profile
Browse files Browse the repository at this point in the history
BugLink: http://bugs.launchpad.net/bugs/1056078

Profile replacement can cause long chains of profiles to build up when
the profile being replaced is pinned. When the pinned profile is finally
freed, it puts the reference to its replacement, which may in turn nest
another call to free_profile on the stack. Because this may happen for
each profile in the replacedby chain this can result in a recusion that
causes the stack to overflow.

Break this nesting by directly walking the chain of replacedby profiles
(ie. use iteration instead of recursion to free the list). This results
in at most 2 levels of free_profile being called, while freeing a
replacedby chain.

Signed-off-by: John Johansen <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
John Johansen authored and James Morris committed Oct 24, 2012
1 parent 0e9e3e3 commit 2e680dd
Showing 1 changed file with 23 additions and 1 deletion.
24 changes: 23 additions & 1 deletion security/apparmor/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
*/
static void free_profile(struct aa_profile *profile)
{
struct aa_profile *p;

AA_DEBUG("%s(%p)\n", __func__, profile);

if (!profile)
Expand Down Expand Up @@ -751,7 +753,27 @@ static void free_profile(struct aa_profile *profile)
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);

aa_put_profile(profile->replacedby);
/* put the profile reference for replacedby, but not via
* put_profile(kref_put).
* replacedby can form a long chain that can result in cascading
* frees that blows the stack because kref_put makes a nested fn
* call (it looks like recursion, with free_profile calling
* free_profile) for each profile in the chain lp#1056078.
*/
for (p = profile->replacedby; p; ) {
if (atomic_dec_and_test(&p->base.count.refcount)) {
/* no more refs on p, grab its replacedby */
struct aa_profile *next = p->replacedby;
/* break the chain */
p->replacedby = NULL;
/* now free p, chain is broken */
free_profile(p);

/* follow up with next profile in the chain */
p = next;
} else
break;
}

kzfree(profile);
}
Expand Down

0 comments on commit 2e680dd

Please sign in to comment.