aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2010-04-13 11:43:58 +0100
committerLeann Ogasawara <leann.ogasawara@canonical.com>2010-06-14 08:51:10 -0700
commitfbc0f9759ae773b4cd41f61e0a8adc52bda13b3b (patch)
tree0a4ded3b0aa077df3cb67ab31562db84b36610c6
parentacfe28126c3e9bd37c64d8a24a4df1d22cf5d2fc (diff)
AppArmor: use the kernel shared workqueue to free vmalloc'ed dfas
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> Acked-by: Stefan Bader <stefan.bader@canonical.com> Acked-by: Andy Whitcroft <andy.whitcroft@canonical.com> Signed-off-by: Andy Whitcroft <apw@canonical.com>
-rw-r--r--security/apparmor/match.c27
1 files changed, 24 insertions, 3 deletions
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index afc2dd2260e..a3730e22010 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);
}
@@ -75,6 +94,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
/* freed by free_table */
table = kmalloc(tsize, GFP_KERNEL | __GFP_NOWARN);
if (!table) {
+ tsize = tsize < sizeof(struct work_struct) ?
+ sizeof(struct work_struct) : tsize;
unmap_alias = 1;
table = vmalloc(tsize);
}