WIP: add AppArmor KUnit test

Add KUnit test as its own file (making necessary changes to adjacent
files for tests to pass).

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index b6b68a7..6073e5d 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -81,3 +81,9 @@
 	  Set the default value of the apparmor.debug kernel parameter.
 	  When enabled, various debug messages will be logged to
 	  the kernel message buffer.
+
+config SECURITY_APPARMOR_TEST
+	bool "Build KUnit tests for policy_unpack.c"
+	default n
+	select SECURITY_APPARMOR
+	select KUNIT
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index ff23fcf..7ac1fc2 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -2,6 +2,7 @@
 # Makefile for AppArmor Linux Security Module
 #
 obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
+obj-$(CONFIG_SECURITY_APPARMOR_TEST) += policy_unpack_test.o
 
 apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
               path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index 8db4ab7..7a96e9e 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -20,6 +20,59 @@
 #include <linux/dcache.h>
 #include <linux/workqueue.h>
 
+/*
+ * The AppArmor interface treats data as a type byte followed by the
+ * actual data.  The interface has the notion of a a named entry
+ * which has a name (AA_NAME typecode followed by name string) followed by
+ * the entries typecode and data.  Named types allow for optional
+ * elements and extensions to be added and tested for without breaking
+ * backwards compatibility.
+ */
+
+enum aa_code {
+	AA_U8,
+	AA_U16,
+	AA_U32,
+	AA_U64,
+	AA_NAME,		/* same as string except it is items name */
+	AA_STRING,
+	AA_BLOB,
+	AA_STRUCT,
+	AA_STRUCTEND,
+	AA_LIST,
+	AA_LISTEND,
+	AA_ARRAY,
+	AA_ARRAYEND,
+};
+
+/*
+ * aa_ext is the read of the buffer containing the serialized profile.  The
+ * data is copied into a kernel buffer in apparmorfs and then handed off to
+ * the unpack routines.
+ */
+struct aa_ext {
+	void *start;
+	void *end;
+	void *pos;		/* pointer to current position in the buffer */
+	u32 version;
+};
+
+/* test if read will be in packed data bounds */
+static inline bool inbounds(struct aa_ext *e, size_t size)
+{
+	return (size <= e->end - e->pos);
+}
+
+size_t unpack_u16_chunk(struct aa_ext *e, char **chunk);
+bool unpack_X(struct aa_ext *e, enum aa_code code);
+bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name);
+bool unpack_u32(struct aa_ext *e, u32 *data, const char *name);
+bool unpack_u64(struct aa_ext *e, u64 *data, const char *name);
+size_t unpack_array(struct aa_ext *e, const char *name);
+size_t unpack_blob(struct aa_ext *e, char **blob, const char *name);
+int unpack_str(struct aa_ext *e, const char **string, const char *name);
+int unpack_strdup(struct aa_ext *e, char **string, const char *name);
+
 struct aa_load_ent {
 	struct list_head list;
 	struct aa_profile *new;
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 21cb384..af9002d 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -40,43 +40,6 @@
 #define v7	7
 #define v8	8	/* full network masking */
 
-/*
- * The AppArmor interface treats data as a type byte followed by the
- * actual data.  The interface has the notion of a a named entry
- * which has a name (AA_NAME typecode followed by name string) followed by
- * the entries typecode and data.  Named types allow for optional
- * elements and extensions to be added and tested for without breaking
- * backwards compatibility.
- */
-
-enum aa_code {
-	AA_U8,
-	AA_U16,
-	AA_U32,
-	AA_U64,
-	AA_NAME,		/* same as string except it is items name */
-	AA_STRING,
-	AA_BLOB,
-	AA_STRUCT,
-	AA_STRUCTEND,
-	AA_LIST,
-	AA_LISTEND,
-	AA_ARRAY,
-	AA_ARRAYEND,
-};
-
-/*
- * aa_ext is the read of the buffer containing the serialized profile.  The
- * data is copied into a kernel buffer in apparmorfs and then handed off to
- * the unpack routines.
- */
-struct aa_ext {
-	void *start;
-	void *end;
-	void *pos;		/* pointer to current position in the buffer */
-	u32 version;
-};
-
 /* audit callback for unpack fields */
 static void audit_cb(struct audit_buffer *ab, void *va)
 {
@@ -198,12 +161,6 @@
 	return d;
 }
 
-/* test if read will be in packed data bounds */
-static bool inbounds(struct aa_ext *e, size_t size)
-{
-	return (size <= e->end - e->pos);
-}
-
 static void *kvmemdup(const void *src, size_t len)
 {
 	void *p = kvmalloc(len, GFP_KERNEL);
@@ -220,7 +177,7 @@
  *
  * Returns: the size of chunk found with the read head at the end of the chunk.
  */
-static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
+size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
 {
 	size_t size = 0;
 
@@ -236,7 +193,7 @@
 }
 
 /* unpack control byte */
-static bool unpack_X(struct aa_ext *e, enum aa_code code)
+bool unpack_X(struct aa_ext *e, enum aa_code code)
 {
 	if (!inbounds(e, 1))
 		return 0;
@@ -262,7 +219,7 @@
  *
  * Returns: 0 if either match fails, the read head does not move
  */
-static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
+bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
 {
 	/*
 	 * May need to reset pos if name or type doesn't match
@@ -292,7 +249,7 @@
 	return 0;
 }
 
-static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
+bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
 {
 	if (unpack_nameX(e, AA_U32, name)) {
 		if (!inbounds(e, sizeof(u32)))
@@ -305,7 +262,7 @@
 	return 0;
 }
 
-static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
+bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
 {
 	if (unpack_nameX(e, AA_U64, name)) {
 		if (!inbounds(e, sizeof(u64)))
@@ -318,7 +275,7 @@
 	return 0;
 }
 
-static size_t unpack_array(struct aa_ext *e, const char *name)
+size_t unpack_array(struct aa_ext *e, const char *name)
 {
 	if (unpack_nameX(e, AA_ARRAY, name)) {
 		int size;
@@ -331,7 +288,7 @@
 	return 0;
 }
 
-static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
+size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
 {
 	if (unpack_nameX(e, AA_BLOB, name)) {
 		u32 size;
@@ -348,7 +305,7 @@
 	return 0;
 }
 
-static int unpack_str(struct aa_ext *e, const char **string, const char *name)
+int unpack_str(struct aa_ext *e, const char **string, const char *name)
 {
 	char *src_str;
 	size_t size = 0;
@@ -370,7 +327,7 @@
 	return 0;
 }
 
-static int unpack_strdup(struct aa_ext *e, char **string, const char *name)
+int unpack_strdup(struct aa_ext *e, char **string, const char *name)
 {
 	const char *tmp;
 	void *pos = e->pos;
diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c
new file mode 100644
index 0000000..98e5c5b
--- /dev/null
+++ b/security/apparmor/policy_unpack_test.c
@@ -0,0 +1,611 @@
+#include <asm/unaligned.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/cred.h"
+#include "include/crypto.h"
+#include "include/match.h"
+#include "include/path.h"
+#include "include/policy.h"
+#include "include/policy_unpack.h"
+
+#ifdef CONFIG_SECURITY_APPARMOR_TEST
+#include <test/test.h>
+#endif
+
+/* A set of unit tests for the functions in policy_unpack.c */
+#ifdef CONFIG_SECURITY_APPARMOR_TEST
+#define TEST_STRING_NAME "TEST_STRING"
+#define TEST_STRING_DATA "testing"
+#define TEST_STRING_BUF_OFFSET (3 + strlen(TEST_STRING_NAME) + 1)
+
+#define TEST_U32_NAME "U32_TEST"
+#define TEST_U32_DATA 0x01020304
+#define TEST_NAMED_U32_BUF_OFFSET TEST_STRING_BUF_OFFSET + 3\
+	+ strlen(TEST_STRING_DATA) + 1
+#define TEST_U32_BUF_OFFSET TEST_NAMED_U32_BUF_OFFSET + 3 \
+	+ strlen(TEST_U32_NAME) + 1
+
+#define TEST_U16_OFFSET TEST_U32_BUF_OFFSET + 3
+#define TEST_U16_DATA (u16) (TEST_U32_DATA >> 16)
+
+#define TEST_U64_NAME "U64_TEST"
+#define TEST_U64_DATA 0x0102030405060708
+#define TEST_NAMED_U64_BUF_OFFSET TEST_U32_BUF_OFFSET + sizeof(u32) + 1
+#define TEST_U64_BUF_OFFSET TEST_NAMED_U64_BUF_OFFSET + 3 \
+	+ strlen(TEST_U64_NAME) + 1
+
+#define TEST_BLOB_NAME "BLOB_TEST"
+#define TEST_BLOB_DATA "\xde\xad\x00\xbe\xef"
+#define TEST_BLOB_DATA_SIZE 5
+#define TEST_NAMED_BLOB_BUF_OFFSET TEST_U64_BUF_OFFSET + sizeof(u64) + 1
+#define TEST_BLOB_BUF_OFFSET TEST_NAMED_BLOB_BUF_OFFSET + 3 \
+		+ strlen(TEST_BLOB_NAME) + 1
+
+#define TEST_ARRAY_NAME "ARRAY_TEST"
+#define TEST_ARRAY_SIZE 16
+#define TEST_NAMED_ARRAY_BUF_OFFSET TEST_BLOB_BUF_OFFSET \
+		+ 5 + TEST_BLOB_DATA_SIZE
+#define TEST_ARRAY_BUF_OFFSET TEST_NAMED_ARRAY_BUF_OFFSET + 3 \
+		+ strlen(TEST_ARRAY_NAME) + 1
+
+struct policy_unpack_fixture {
+	struct aa_ext *e;
+	size_t e_size;
+};
+
+struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
+				   struct test *test, size_t buf_size)
+{
+	void *buf;
+	struct aa_ext *e;
+
+	buf = test_kmalloc(test, buf_size, GFP_USER);
+	ASSERT_NOT_ERR_OR_NULL(test, buf);
+
+	e = test_kmalloc(test, sizeof(*e), GFP_USER);
+	ASSERT_NOT_ERR_OR_NULL(test, e);
+
+	memset(buf, 0, buf_size);
+
+	e->start = buf;
+	e->end = e->start + buf_size;
+	e->pos = e->start;
+
+	*(char*) buf = AA_NAME;
+	*(char*) (buf + 1) = strlen(TEST_STRING_NAME) + 1;
+	strcpy(buf + 3, TEST_STRING_NAME);
+
+	buf = e->start + TEST_STRING_BUF_OFFSET;
+	*(char*) buf = AA_STRING;
+	*(char*) (buf + 1) = strlen(TEST_STRING_DATA) + 1;
+	strcpy(buf + 3, TEST_STRING_DATA);
+
+	buf = e->start + TEST_NAMED_U32_BUF_OFFSET;
+	*(char*) buf = AA_NAME;
+	*(char*) (buf + 1) = strlen(TEST_U32_NAME) + 1;
+	strcpy(buf + 3, TEST_U32_NAME);
+	*(char*) (buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32;
+	*(u32*) (buf + 3 + strlen(TEST_U32_NAME) + 2) = TEST_U32_DATA;
+
+	buf = e->start + TEST_NAMED_U64_BUF_OFFSET;
+	*(char*) buf = AA_NAME;
+	*(char*) (buf + 1) = strlen(TEST_U64_NAME) + 1;
+	strcpy(buf + 3, TEST_U64_NAME);
+	*(char*) (buf + 3 + strlen(TEST_U64_NAME) + 1) = AA_U64;
+	*(u64*) (buf + 3 + strlen(TEST_U64_NAME) + 2) = TEST_U64_DATA;
+
+	buf = e->start + TEST_NAMED_BLOB_BUF_OFFSET;
+	*(char*) buf = AA_NAME;
+	*(char*) (buf + 1) = strlen(TEST_BLOB_NAME) + 1;
+	strcpy(buf + 3, TEST_BLOB_NAME);
+	*(char*) (buf + 3 + strlen(TEST_BLOB_NAME) + 1) = AA_BLOB;
+	*(char*) (buf + 3 + strlen(TEST_BLOB_NAME) + 2) = TEST_BLOB_DATA_SIZE;
+	memcpy(buf + 3 + strlen(TEST_BLOB_NAME) + 6,
+		TEST_BLOB_DATA, TEST_BLOB_DATA_SIZE);
+
+	buf = e->start + TEST_NAMED_ARRAY_BUF_OFFSET;
+	*(char*) buf = AA_NAME;
+	*(char*) (buf + 1) = strlen(TEST_ARRAY_NAME) + 1;
+	strcpy(buf + 3, TEST_ARRAY_NAME);
+	*(char*) (buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY;
+	*(u16*) (buf + 3 + strlen(TEST_ARRAY_NAME) + 2) = TEST_ARRAY_SIZE;
+
+	return e;
+}
+
+static int policy_unpack_test_init(struct test *test)
+{
+	size_t e_size = TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1;
+
+	struct policy_unpack_fixture *puf;
+	puf = test_kmalloc(test, sizeof(*puf), GFP_USER);
+	ASSERT_NOT_ERR_OR_NULL(test, puf);
+
+	puf->e_size = e_size;
+	puf->e = build_aa_ext_struct(puf, test, e_size);
+
+	test->priv = puf;
+	return 0;
+}
+
+static void inbounds_test_inbounds(struct test *test)
+{
+	struct policy_unpack_fixture *puf = test->priv;
+
+	ASSERT_TRUE(test, inbounds(puf->e, 0));
+	ASSERT_TRUE(test, inbounds(puf->e, puf->e_size / 2));
+	ASSERT_TRUE(test, inbounds(puf->e, puf->e_size));
+}
+
+static void inbounds_test_out_of_bounds(struct test *test)
+{
+	struct policy_unpack_fixture *puf = test->priv;
+
+	ASSERT_FALSE(test, inbounds(puf->e, puf->e_size + 1));
+}
+
+static void unpack_array_test_null_name(struct test *test)
+{
+	u16 array_size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_ARRAY_BUF_OFFSET;
+
+	array_size = unpack_array(puf->e, NULL);
+
+	EXPECT_EQ(test, array_size, TEST_ARRAY_SIZE);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
+}
+
+static void unpack_array_test_with_name(struct test *test)
+{
+	u16 array_size;
+	const char name[] = TEST_ARRAY_NAME;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
+
+	array_size = unpack_array(puf->e, name);
+
+	EXPECT_EQ(test, array_size, TEST_ARRAY_SIZE);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
+}
+
+static void unpack_array_test_out_of_bounds(struct test *test)
+{
+	u16 array_size;
+	const char name[] = TEST_ARRAY_NAME;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
+	puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16);
+
+	array_size = unpack_array(puf->e, name);
+
+	EXPECT_EQ(test, array_size, 0);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET);
+}
+
+static void unpack_blob_test_null_name(struct test *test)
+{
+	char *blob = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_BLOB_BUF_OFFSET;
+	size = unpack_blob(puf->e, &blob, NULL);
+
+	EXPECT_EQ(test, size, TEST_BLOB_DATA_SIZE);
+	EXPECT_TRUE(test,
+		memcmp(blob, TEST_BLOB_DATA, TEST_BLOB_DATA_SIZE) == 0);
+}
+
+static void unpack_blob_test_with_name(struct test *test)
+{
+	char *blob = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_BLOB_BUF_OFFSET;
+	size = unpack_blob(puf->e, &blob, TEST_BLOB_NAME);
+
+	EXPECT_EQ(test, size, TEST_BLOB_DATA_SIZE);
+	EXPECT_TRUE(test,
+		memcmp(blob, TEST_BLOB_DATA, TEST_BLOB_DATA_SIZE) == 0);
+}
+
+static void unpack_blob_test_out_of_bounds(struct test *test)
+{
+	char *blob = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+	void *start;
+
+	puf->e->pos += TEST_NAMED_BLOB_BUF_OFFSET;
+	start = puf->e->pos;
+	puf->e->end = puf->e->start + TEST_BLOB_BUF_OFFSET
+		+ TEST_BLOB_DATA_SIZE - 1;
+
+	size = unpack_blob(puf->e, &blob, TEST_BLOB_NAME);
+
+	EXPECT_EQ(test, size, 0);
+	EXPECT_EQ(test, puf->e->pos, start);
+}
+
+static void unpack_str_test_null_name(struct test *test)
+{
+	const char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_STRING_BUF_OFFSET;
+	size = unpack_str(puf->e, &string, NULL);
+
+	EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1);
+	EXPECT_STREQ(test, string, TEST_STRING_DATA);
+}
+
+static void unpack_str_test_with_name(struct test *test)
+{
+	const char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	size = unpack_str(puf->e, &string, TEST_STRING_NAME);
+
+	EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1);
+	EXPECT_STREQ(test, string, TEST_STRING_DATA);
+}
+
+static void unpack_str_test_out_of_bounds(struct test *test)
+{
+	const char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	void *start = puf->e->pos;
+	puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET
+		+ strlen(TEST_STRING_DATA) - 1;
+
+	size = unpack_str(puf->e, &string, TEST_STRING_NAME);
+
+	EXPECT_EQ(test, size, 0);
+	EXPECT_EQ(test, puf->e->pos, start);
+}
+
+static void unpack_strdup_test_null_name(struct test *test)
+{
+	char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_STRING_BUF_OFFSET;
+	size = unpack_strdup(puf->e, &string, NULL);
+
+	EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1);
+	EXPECT_FALSE(test, ((uintptr_t)puf->e->start <= (uintptr_t)string)
+				&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
+	EXPECT_STREQ(test, string, TEST_STRING_DATA);
+}
+
+static void unpack_strdup_test_with_name(struct test *test)
+{
+	char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	size = unpack_strdup(puf->e, &string, TEST_STRING_NAME);
+
+	EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1);
+	EXPECT_FALSE(test, ((uintptr_t)puf->e->start <= (uintptr_t)string)
+				&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
+	EXPECT_STREQ(test, string, TEST_STRING_DATA);
+}
+
+static void unpack_strdup_test_out_of_bounds(struct test *test)
+{
+	char *string = NULL;
+	int size;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	void *start = puf->e->pos;
+	puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET
+		+ strlen(TEST_STRING_DATA) - 1;
+
+	size = unpack_strdup(puf->e, &string, TEST_STRING_NAME);
+
+	EXPECT_EQ(test, size, 0);
+	EXPECT_EQ(test, string, NULL);
+	EXPECT_EQ(test, puf->e->pos, start);
+}
+
+static void unpack_nameX_test_null_name(struct test *test)
+{
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U32_BUF_OFFSET;
+
+	success = unpack_nameX(puf->e, AA_U32, NULL);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, puf->e->pos, puf->e->start + TEST_U32_BUF_OFFSET + 1);
+}
+
+static void unpack_nameX_test_null_name_wrong_code(struct test *test)
+{
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U32_BUF_OFFSET;
+
+	success = unpack_nameX(puf->e, AA_BLOB, NULL);
+
+	EXPECT_FALSE(test, success);
+	EXPECT_EQ(test, puf->e->pos, puf->e->start + TEST_U32_BUF_OFFSET);
+}
+
+static void unpack_nameX_test_with_name(struct test *test)
+{
+	const char name[] = TEST_U32_NAME;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U32_BUF_OFFSET;
+
+	success = unpack_nameX(puf->e, AA_U32, name);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, puf->e->pos, puf->e->start + TEST_U32_BUF_OFFSET + 1);
+}
+
+static void unpack_nameX_test_wrong_name(struct test *test)
+{
+	const char name[] = "12345678";
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U32_BUF_OFFSET;
+
+	success = unpack_nameX(puf->e, AA_U32, name);
+
+	EXPECT_FALSE(test, success);
+	EXPECT_EQ(test, puf->e->pos, puf->e->start + TEST_NAMED_U32_BUF_OFFSET);
+}
+
+static void unpack_u16_chunk_test(struct test *test)
+{
+	size_t size;
+	char *chunk = NULL;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U16_OFFSET;
+	/*
+	 * WARNING: For unit testing purposes, we're pushing puf->e->end past the
+	 * end of the allocated memory. Doing anything other than comparing memory
+	 * addresses is dangerous.
+	 */
+	puf->e->end += TEST_U16_DATA;
+
+	size = unpack_u16_chunk(puf->e, &chunk);
+
+	EXPECT_EQ(test, chunk, puf->e->start + TEST_U16_OFFSET + 2);
+	EXPECT_EQ(test, size, TEST_U16_DATA);
+	EXPECT_EQ(test, puf->e->pos, chunk + TEST_U16_DATA);
+}
+
+static void unpack_u16_chunk_test_out_of_bounds_1(struct test *test)
+{
+	size_t size;
+	char *chunk = NULL;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos = puf->e->end - 1;
+
+	size = unpack_u16_chunk(puf->e, &chunk);
+
+	EXPECT_EQ(test, size, 0);
+	EXPECT_EQ(test, chunk, NULL);
+	EXPECT_EQ(test, puf->e->pos, puf->e->end - 1);
+}
+
+static void unpack_u16_chunk_test_out_of_bounds_2(struct test *test)
+{
+	size_t size;
+	char *chunk = NULL;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U16_OFFSET;
+	/*
+	 * WARNING: For unit testing purposes, we're pushing puf->e->end past the
+	 * end of the allocated memory. Doing anything other than comparing memory
+	 * addresses is dangerous.
+	 */
+	puf->e->end = puf->e->pos + TEST_U16_DATA - 1;
+
+	size = unpack_u16_chunk(puf->e, &chunk);
+
+	EXPECT_EQ(test, size, 0);
+	EXPECT_EQ(test, chunk, NULL);
+	EXPECT_EQ(test, puf->e->pos, puf->e->start + TEST_U16_OFFSET);
+}
+
+static void unpack_u32_test_null_name(struct test *test)
+{
+	u32 data;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U32_BUF_OFFSET;
+
+	success = unpack_u32(puf->e, &data, NULL);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, data, TEST_U32_DATA);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32) + 1);
+}
+
+static void unpack_u32_test_with_name(struct test *test)
+{
+	u32 data;
+	const char name[] = TEST_U32_NAME;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U32_BUF_OFFSET;
+
+	success = unpack_u32(puf->e, &data, name);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, data, TEST_U32_DATA);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32) + 1);
+}
+
+static void unpack_u32_test_out_of_bounds(struct test *test)
+{
+	u32 data;
+	const char name[] = TEST_U32_NAME;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U32_BUF_OFFSET;
+	puf->e->end = puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32);
+
+	success = unpack_u32(puf->e, &data, name);
+
+	EXPECT_FALSE(test, success);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_NAMED_U32_BUF_OFFSET);
+}
+
+static void unpack_u64_test_null_name(struct test *test)
+{
+	u64 data;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_U64_BUF_OFFSET;
+
+	success = unpack_u64(puf->e, &data, NULL);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, data, TEST_U64_DATA);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64) + 1);
+}
+
+static void unpack_u64_test_with_name(struct test *test)
+{
+	u64 data;
+	const char name[] = TEST_U64_NAME;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U64_BUF_OFFSET;
+
+	success = unpack_u64(puf->e, &data, name);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_EQ(test, data, TEST_U64_DATA);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64) + 1);
+}
+
+static void unpack_u64_test_out_of_bounds(struct test *test)
+{
+	u64 data;
+	const char name[] = TEST_U64_NAME;
+	bool success;
+	struct policy_unpack_fixture *puf = test->priv;
+
+	puf->e->pos += TEST_NAMED_U64_BUF_OFFSET;
+	puf->e->end = puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64);
+
+	success = unpack_u64(puf->e, &data, name);
+
+	EXPECT_FALSE(test, success);
+	EXPECT_EQ(test, puf->e->pos,
+			puf->e->start + TEST_NAMED_U64_BUF_OFFSET);
+}
+
+static void unpack_X_test_code_match(struct test *test)
+{
+	struct policy_unpack_fixture *puf = test->priv;
+
+	bool success = unpack_X(puf->e, AA_NAME);
+
+	EXPECT_TRUE(test, success);
+	EXPECT_TRUE(test, puf->e->pos == puf->e->start + 1);
+}
+
+static void unpack_X_test_code_mismatch(struct test *test)
+{
+	struct policy_unpack_fixture *puf = test->priv;
+
+	bool success = unpack_X(puf->e, AA_STRING);
+
+	EXPECT_FALSE(test, success);
+	EXPECT_TRUE(test, puf->e->pos == puf->e->start);
+}
+
+static void unpack_X_test_out_of_bounds(struct test *test)
+{
+	struct policy_unpack_fixture *puf = test->priv;
+	bool success;
+
+	puf->e->pos = puf->e->end;
+	success = unpack_X(puf->e, AA_NAME);
+
+	ASSERT_FALSE(test, success);
+}
+
+static struct test_case apparmor_policy_unpack_test_cases[] = {
+	TEST_CASE(inbounds_test_inbounds),
+	TEST_CASE(inbounds_test_out_of_bounds),
+	TEST_CASE(unpack_array_test_null_name),
+	TEST_CASE(unpack_array_test_with_name),
+	TEST_CASE(unpack_array_test_out_of_bounds),
+	TEST_CASE(unpack_blob_test_null_name),
+	TEST_CASE(unpack_blob_test_with_name),
+	TEST_CASE(unpack_blob_test_out_of_bounds),
+	TEST_CASE(unpack_nameX_test_null_name),
+	TEST_CASE(unpack_nameX_test_null_name_wrong_code),
+	TEST_CASE(unpack_nameX_test_with_name),
+	TEST_CASE(unpack_nameX_test_wrong_name),
+	TEST_CASE(unpack_str_test_null_name),
+	TEST_CASE(unpack_str_test_with_name),
+	TEST_CASE(unpack_str_test_out_of_bounds),
+	TEST_CASE(unpack_strdup_test_null_name),
+	TEST_CASE(unpack_strdup_test_with_name),
+	TEST_CASE(unpack_strdup_test_out_of_bounds),
+	TEST_CASE(unpack_u16_chunk_test),
+	TEST_CASE(unpack_u16_chunk_test_out_of_bounds_1),
+	TEST_CASE(unpack_u16_chunk_test_out_of_bounds_2),
+	TEST_CASE(unpack_u32_test_null_name),
+	TEST_CASE(unpack_u32_test_with_name),
+	TEST_CASE(unpack_u32_test_out_of_bounds),
+	TEST_CASE(unpack_u64_test_null_name),
+	TEST_CASE(unpack_u64_test_with_name),
+	TEST_CASE(unpack_u64_test_out_of_bounds),
+	TEST_CASE(unpack_X_test_code_match),
+	TEST_CASE(unpack_X_test_code_mismatch),
+	TEST_CASE(unpack_X_test_out_of_bounds),
+	{},
+};
+
+static struct test_module apparmor_policy_unpack_test_module = {
+	.name = "apparmor_policy_unpack",
+	.init = policy_unpack_test_init,
+	.test_cases = apparmor_policy_unpack_test_cases,
+};
+
+module_test(apparmor_policy_unpack_test_module);
+#endif