diff options
author | John Johansen <john.johansen@canonical.com> | 2010-04-13 11:43:58 +0100 |
---|---|---|
committer | Leann Ogasawara <leann.ogasawara@canonical.com> | 2010-06-14 08:51:10 -0700 |
commit | fbc0f9759ae773b4cd41f61e0a8adc52bda13b3b (patch) | |
tree | 0a4ded3b0aa077df3cb67ab31562db84b36610c6 | |
parent | acfe28126c3e9bd37c64d8a24a4df1d22cf5d2fc (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.c | 27 |
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); } |