From: John Johansen <john.johansen@canonical.com>
Date: Tue, 20 Jul 2010 06:57:08 -0700
Subject: AppArmor: Allow dfa backward compatibility with broken userspace
Patch-mainline: 2.6.37?

The apparmor_parser when compiling policy could generate invalid dfas
that did not have sufficient padding to avoid invalid references, when
used by the kernel.  The kernels check to verify the next/check table
size was broken meaning invalid dfas were being created by userspace
and not caught.

To remain compatible with old tools that are not fixed, pad the loaded
dfas next/check table.  The dfa's themselves are valid except for the
high padding for potentially invalid transitions (high bounds error),
which have a maximimum is 256 entries.  So just allocate an extra null filled
256 entries for the next/check tables.  This will guarentee all bounds
are good and invalid transitions go to the null (0) state.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Jeff Mahoney <jeffm@suse.com>
---
 security/apparmor/match.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)

--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -57,8 +57,17 @@ static struct table_header *unpack_table
 	if (bsize < tsize)
 		goto out;
 
+	/* Pad table allocation for next/check by 256 entries to remain
+	 * backwards compatible with old (buggy) tools and remain safe without
+	 * run time checks
+	 */
+	if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
+		tsize += 256 * th.td_flags;
+
 	table = kvmalloc(tsize);
 	if (table) {
+		/* ensure the pad is clear, else there will be errors */
+		memset(table, 0, tsize);
 		*table = th;
 		if (th.td_flags == YYTD_DATA8)
 			UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa
 		goto out;
 
 	if (flags & DFA_FLAG_VERIFY_STATES) {
+		int warning = 0;
 		for (i = 0; i < state_count; i++) {
 			if (DEFAULT_TABLE(dfa)[i] >= state_count)
 				goto out;
 			/* TODO: do check that DEF state recursion terminates */
 			if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
+				if (warning)
+					continue;
+				printk(KERN_WARNING "AppArmor DFA next/check "
+				       "upper bounds error fixed, upgrade "
+				       "user space tools \n");
+				warning = 1;
+			} else if (BASE_TABLE(dfa)[i] >= trans_count) {
 				printk(KERN_ERR "AppArmor DFA next/check upper "
 				       "bounds error\n");
 				goto out;
