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/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 07092e8..97cd683 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 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size) 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 @@ static void *kvmemdup(const void *src, size_t len) * * 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; void *pos = e->pos; @@ -241,7 +198,7 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) } /* 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; @@ -267,7 +224,7 @@ static bool unpack_X(struct aa_ext *e, enum aa_code code) * * 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 @@ -297,7 +254,7 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) 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) { void *pos = e->pos; @@ -315,7 +272,7 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) 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) { void *pos = e->pos; @@ -333,7 +290,7 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) 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) { void *pos = e->pos; @@ -351,7 +308,7 @@ static size_t unpack_array(struct aa_ext *e, const char *name) 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) { void *pos = e->pos; @@ -373,7 +330,7 @@ static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) 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; @@ -396,7 +353,7 @@ static int unpack_str(struct aa_ext *e, const char **string, const char *name) 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