diff mbox

[11/11] AppArmor: use the kernel shared workqueue to free vmalloc'ed dfas

Message ID 1271142580-26555-12-git-send-email-john.johansen@canonical.com
State Superseded
Delegated to: Andy Whitcroft
Headers show

Commit Message

John Johansen April 13, 2010, 7:09 a.m. UTC
From: John Johansen <john.johansen@canonical.com>

OriginalAuthor: John Johansen <john.johansen@canonical.com>
OriginalLocation: git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor.git AA2.5-2.6.33
commit: 2b0c4ab1a947adfef6a09619454b7f8eac46df96
BugLink: http://bugs.launchpad.net/bugs/562044

AppArmor falls back to allocating dfas with vmalloc when memory becomes
fragmented.  However dfa life cycle is often dependent on credentials
which can be freed during interrupt context.  This can in cause the dfa
to be freed during interrupt context, as well but vfree can not be
used in interrupt context.

So for dfas that are allocated with vmalloc delay freeing them to
a later time by placing them on the kernel shared workqueue.

Signed-off-by: John Johansen <john.johansen@canonical.com>
---
 security/apparmor/match.c |   25 ++++++++++++++++++++++---
 1 files changed, 22 insertions(+), 3 deletions(-)

Comments

Stefan Bader April 13, 2010, 8:22 a.m. UTC | #1
Dunno, this looks a bit scary. Assuming the size of table is big enough to
contain a work struct, but is the struct really never touched again after
calling the worker function?

john.johansen@canonical.com wrote:
> From: John Johansen <john.johansen@canonical.com>
> 
> OriginalAuthor: John Johansen <john.johansen@canonical.com>
> OriginalLocation: git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor.git AA2.5-2.6.33
> commit: 2b0c4ab1a947adfef6a09619454b7f8eac46df96
> BugLink: http://bugs.launchpad.net/bugs/562044
> 
> AppArmor falls back to allocating dfas with vmalloc when memory becomes
> fragmented.  However dfa life cycle is often dependent on credentials
> which can be freed during interrupt context.  This can in cause the dfa
> to be freed during interrupt context, as well but vfree can not be
> used in interrupt context.
> 
> So for dfas that are allocated with vmalloc delay freeing them to
> a later time by placing them on the kernel shared workqueue.
> 
> Signed-off-by: John Johansen <john.johansen@canonical.com>
> ---
>  security/apparmor/match.c |   25 ++++++++++++++++++++++---
>  1 files changed, 22 insertions(+), 3 deletions(-)
> 
> diff --git a/security/apparmor/match.c b/security/apparmor/match.c
> index afc2dd2..d2cd554 100644
> --- a/security/apparmor/match.c
> +++ b/security/apparmor/match.c
> @@ -17,12 +17,26 @@
>  #include <linux/mm.h>
>  #include <linux/slab.h>
>  #include <linux/vmalloc.h>
> +#include <linux/workqueue.h>
>  #include <linux/err.h>
>  #include <linux/kref.h>
>  
>  #include "include/match.h"
>  
>  /**
> + * do_vfree - workqueue routine for freeing vmalloced memory
> + * @work: data to be freed
> + *
> + * The work_struct is overlayed to the data being freed, as at the point
> + * the work is scheduled the data is no longer valid, be its freeing
> + * needs to be delayed until safe.
> + */
> +static void do_vfree(struct work_struct *work)
> +{
> +	vfree(work);
> +}
> +
> +/**
>   * free_table - free a table allocated by unpack table
>   * @table: table to unpack  (MAYBE NULL)
>   */
> @@ -31,9 +45,14 @@ static void free_table(struct table_header *table)
>  	if (!table)
>  		return;
>  
> -	if (is_vmalloc_addr(table))
> -		vfree(table);
> -	else
> +	if (is_vmalloc_addr(table)) {
> +		/* Data is no longer valid so just use the allocated space
> +		 * as the work_struct
> +		 */
> +		struct work_struct *work = (struct work_struct *) table;
> +		INIT_WORK(work, do_vfree);
> +		schedule_work(work);	  
> +	} else
>  		kzfree(table);
>  }
>
John Johansen April 13, 2010, 8:31 a.m. UTC | #2
On 04/13/2010 01:22 AM, Stefan Bader wrote:
> Dunno, this looks a bit scary. Assuming the size of table is big enough to
> contain a work struct,
well yes the size is big enough, but we could bound the table size on alloc
so it is documented in the code.

> but is the struct really never touched again after
> calling the worker function?
> 
If it is its a bug.  The only way this code gets reached is if all the
refcounts on the profile and the dfa have been put.

> john.johansen@canonical.com wrote:
>> From: John Johansen <john.johansen@canonical.com>
>>
>> OriginalAuthor: John Johansen <john.johansen@canonical.com>
>> OriginalLocation: git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor.git AA2.5-2.6.33
>> commit: 2b0c4ab1a947adfef6a09619454b7f8eac46df96
>> BugLink: http://bugs.launchpad.net/bugs/562044
>>
>> AppArmor falls back to allocating dfas with vmalloc when memory becomes
>> fragmented.  However dfa life cycle is often dependent on credentials
>> which can be freed during interrupt context.  This can in cause the dfa
>> to be freed during interrupt context, as well but vfree can not be
>> used in interrupt context.
>>
>> So for dfas that are allocated with vmalloc delay freeing them to
>> a later time by placing them on the kernel shared workqueue.
>>
>> Signed-off-by: John Johansen <john.johansen@canonical.com>
>> ---
>>  security/apparmor/match.c |   25 ++++++++++++++++++++++---
>>  1 files changed, 22 insertions(+), 3 deletions(-)
>>
>> diff --git a/security/apparmor/match.c b/security/apparmor/match.c
>> index afc2dd2..d2cd554 100644
>> --- a/security/apparmor/match.c
>> +++ b/security/apparmor/match.c
>> @@ -17,12 +17,26 @@
>>  #include <linux/mm.h>
>>  #include <linux/slab.h>
>>  #include <linux/vmalloc.h>
>> +#include <linux/workqueue.h>
>>  #include <linux/err.h>
>>  #include <linux/kref.h>
>>  
>>  #include "include/match.h"
>>  
>>  /**
>> + * do_vfree - workqueue routine for freeing vmalloced memory
>> + * @work: data to be freed
>> + *
>> + * The work_struct is overlayed to the data being freed, as at the point
>> + * the work is scheduled the data is no longer valid, be its freeing
>> + * needs to be delayed until safe.
>> + */
>> +static void do_vfree(struct work_struct *work)
>> +{
>> +	vfree(work);
>> +}
>> +
>> +/**
>>   * free_table - free a table allocated by unpack table
>>   * @table: table to unpack  (MAYBE NULL)
>>   */
>> @@ -31,9 +45,14 @@ static void free_table(struct table_header *table)
>>  	if (!table)
>>  		return;
>>  
>> -	if (is_vmalloc_addr(table))
>> -		vfree(table);
>> -	else
>> +	if (is_vmalloc_addr(table)) {
>> +		/* Data is no longer valid so just use the allocated space
>> +		 * as the work_struct
>> +		 */
>> +		struct work_struct *work = (struct work_struct *) table;
>> +		INIT_WORK(work, do_vfree);
>> +		schedule_work(work);	  
>> +	} else
>>  		kzfree(table);
>>  }
>>  
>
Andy Whitcroft April 13, 2010, 9:22 a.m. UTC | #3
On Tue, Apr 13, 2010 at 12:09:40AM -0700, john.johansen@canonical.com wrote:
> From: John Johansen <john.johansen@canonical.com>
> 
> OriginalAuthor: John Johansen <john.johansen@canonical.com>
> OriginalLocation: git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor.git AA2.5-2.6.33
> commit: 2b0c4ab1a947adfef6a09619454b7f8eac46df96
> BugLink: http://bugs.launchpad.net/bugs/562044
> 
> AppArmor falls back to allocating dfas with vmalloc when memory becomes
> fragmented.  However dfa life cycle is often dependent on credentials
> which can be freed during interrupt context.  This can in cause the dfa
> to be freed during interrupt context, as well but vfree can not be
> used in interrupt context.
> 
> So for dfas that are allocated with vmalloc delay freeing them to
> a later time by placing them on the kernel shared workqueue.
> 
> Signed-off-by: John Johansen <john.johansen@canonical.com>
> ---
>  security/apparmor/match.c |   25 ++++++++++++++++++++++---
>  1 files changed, 22 insertions(+), 3 deletions(-)
> 
> diff --git a/security/apparmor/match.c b/security/apparmor/match.c
> index afc2dd2..d2cd554 100644
> --- a/security/apparmor/match.c
> +++ b/security/apparmor/match.c
> @@ -17,12 +17,26 @@
>  #include <linux/mm.h>
>  #include <linux/slab.h>
>  #include <linux/vmalloc.h>
> +#include <linux/workqueue.h>
>  #include <linux/err.h>
>  #include <linux/kref.h>
>  
>  #include "include/match.h"
>  
>  /**
> + * do_vfree - workqueue routine for freeing vmalloced memory
> + * @work: data to be freed
> + *
> + * The work_struct is overlayed to the data being freed, as at the point
> + * the work is scheduled the data is no longer valid, be its freeing
> + * needs to be delayed until safe.
> + */
> +static void do_vfree(struct work_struct *work)
> +{
> +	vfree(work);
> +}
> +
> +/**
>   * free_table - free a table allocated by unpack table
>   * @table: table to unpack  (MAYBE NULL)
>   */
> @@ -31,9 +45,14 @@ static void free_table(struct table_header *table)
>  	if (!table)
>  		return;
>  
> -	if (is_vmalloc_addr(table))
> -		vfree(table);
> -	else
> +	if (is_vmalloc_addr(table)) {
> +		/* Data is no longer valid so just use the allocated space
> +		 * as the work_struct
> +		 */
> +		struct work_struct *work = (struct work_struct *) table;
> +		INIT_WORK(work, do_vfree);
> +		schedule_work(work);	  
> +	} else
>  		kzfree(table);
>  }

This does assume that a dfa cannnot ever be smaller than a struct
work_struct ... is that guarenteed?

-apw
diff mbox

Patch

diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index afc2dd2..d2cd554 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -17,12 +17,26 @@ 
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/workqueue.h>
 #include <linux/err.h>
 #include <linux/kref.h>
 
 #include "include/match.h"
 
 /**
+ * do_vfree - workqueue routine for freeing vmalloced memory
+ * @work: data to be freed
+ *
+ * The work_struct is overlayed to the data being freed, as at the point
+ * the work is scheduled the data is no longer valid, be its freeing
+ * needs to be delayed until safe.
+ */
+static void do_vfree(struct work_struct *work)
+{
+	vfree(work);
+}
+
+/**
  * free_table - free a table allocated by unpack table
  * @table: table to unpack  (MAYBE NULL)
  */
@@ -31,9 +45,14 @@  static void free_table(struct table_header *table)
 	if (!table)
 		return;
 
-	if (is_vmalloc_addr(table))
-		vfree(table);
-	else
+	if (is_vmalloc_addr(table)) {
+		/* Data is no longer valid so just use the allocated space
+		 * as the work_struct
+		 */
+		struct work_struct *work = (struct work_struct *) table;
+		INIT_WORK(work, do_vfree);
+		schedule_work(work);	  
+	} else
 		kzfree(table);
 }