diff mbox

[Precise,SRU] UBUNTU SAUCE: apparmor: fix IRQ stack overflow

Message ID 1348675174-95879-1-git-send-email-tim.gardner@canonical.com
State New
Headers show

Commit Message

Tim Gardner Sept. 26, 2012, 3:59 p.m. UTC
From: John Johansen <john.johansen@canonical.com>

BugLink: http://bugs.launchpad.net/bugs/1056078

Profile replacement can cause a long chain of profiles to build up
that get freed in a cascading chain of free_profile calls. Because
free_profile is being called via aa_put_profile (and hence kref_put)
each profile free is done via what amounts to recursion. That is
free_profile indirectly calls free_profile on the next profile in the
chain via aa_put_profile.

Break this recursion by directly walking the chain, and as long as
a profile is being freed because it has no more references continue
on to the next profile. This results in at most 2 levels of free_profile
being called.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
 security/apparmor/policy.c |   24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

Comments

Brad Figg Sept. 26, 2012, 4:12 p.m. UTC | #1
On 09/26/2012 08:59 AM, Tim Gardner wrote:
> From: John Johansen <john.johansen@canonical.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1056078
> 
> Profile replacement can cause a long chain of profiles to build up
> that get freed in a cascading chain of free_profile calls. Because
> free_profile is being called via aa_put_profile (and hence kref_put)
> each profile free is done via what amounts to recursion. That is
> free_profile indirectly calls free_profile on the next profile in the
> chain via aa_put_profile.
> 
> Break this recursion by directly walking the chain, and as long as
> a profile is being freed because it has no more references continue
> on to the next profile. This results in at most 2 levels of free_profile
> being called.
> 
> Signed-off-by: John Johansen <john.johansen@canonical.com>
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>  security/apparmor/policy.c |   24 +++++++++++++++++++++++-
>  1 file changed, 23 insertions(+), 1 deletion(-)
> 
> diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
> index 4a5e1b5..93faca0 100644
> --- a/security/apparmor/policy.c
> +++ b/security/apparmor/policy.c
> @@ -724,6 +724,8 @@ fail:
>   */
>  static void free_profile(struct aa_profile *profile)
>  {
> +	struct aa_profile *p;
> +
>  	AA_DEBUG("%s(%p)\n", __func__, profile);
>  
>  	if (!profile)
> @@ -752,7 +754,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, but not via put_profile/kref_put
> +	 * replacedby can form a long chain that can result in cascading
> +	 * frees that blows the stack lp#1056078. The long chain creation
> +	 * should be addressed in profile replacement.
> +	 * This just addresses recursion of free_profile causing the
> +	 * stack to blow.
> +	 */
> +	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);
>  }
>
Colin Ian King Sept. 26, 2012, 4:14 p.m. UTC | #2
On 26/09/12 16:59, Tim Gardner wrote:
> From: John Johansen <john.johansen@canonical.com>
>
> BugLink: http://bugs.launchpad.net/bugs/1056078
>
> Profile replacement can cause a long chain of profiles to build up
> that get freed in a cascading chain of free_profile calls. Because
> free_profile is being called via aa_put_profile (and hence kref_put)
> each profile free is done via what amounts to recursion. That is
> free_profile indirectly calls free_profile on the next profile in the
> chain via aa_put_profile.
>
> Break this recursion by directly walking the chain, and as long as
> a profile is being freed because it has no more references continue
> on to the next profile. This results in at most 2 levels of free_profile
> being called.
>
> Signed-off-by: John Johansen <john.johansen@canonical.com>
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>   security/apparmor/policy.c |   24 +++++++++++++++++++++++-
>   1 file changed, 23 insertions(+), 1 deletion(-)
>
> diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
> index 4a5e1b5..93faca0 100644
> --- a/security/apparmor/policy.c
> +++ b/security/apparmor/policy.c
> @@ -724,6 +724,8 @@ fail:
>    */
>   static void free_profile(struct aa_profile *profile)
>   {
> +	struct aa_profile *p;
> +
>   	AA_DEBUG("%s(%p)\n", __func__, profile);
>
>   	if (!profile)
> @@ -752,7 +754,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, but not via put_profile/kref_put
> +	 * replacedby can form a long chain that can result in cascading
> +	 * frees that blows the stack lp#1056078. The long chain creation
> +	 * should be addressed in profile replacement.
> +	 * This just addresses recursion of free_profile causing the
> +	 * stack to blow.
> +	 */
> +	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);
>   }
>
Acked-by: Colin Ian King <colin.king@canonical.com>
Tim Gardner Sept. 26, 2012, 4:42 p.m. UTC | #3

diff mbox

Patch

diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4a5e1b5..93faca0 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -724,6 +724,8 @@  fail:
  */
 static void free_profile(struct aa_profile *profile)
 {
+	struct aa_profile *p;
+
 	AA_DEBUG("%s(%p)\n", __func__, profile);
 
 	if (!profile)
@@ -752,7 +754,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, but not via put_profile/kref_put
+	 * replacedby can form a long chain that can result in cascading
+	 * frees that blows the stack lp#1056078. The long chain creation
+	 * should be addressed in profile replacement.
+	 * This just addresses recursion of free_profile causing the
+	 * stack to blow.
+	 */
+	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);
 }