diff options
Diffstat (limited to 'security/apparmor')
-rw-r--r-- | security/apparmor/Kconfig | 16 | ||||
-rw-r--r-- | security/apparmor/apparmorfs.c | 8 | ||||
-rw-r--r-- | security/apparmor/domain.c | 82 | ||||
-rw-r--r-- | security/apparmor/file.c | 12 | ||||
-rw-r--r-- | security/apparmor/mount.c | 2 | ||||
-rw-r--r-- | security/apparmor/policy.c | 4 | ||||
-rw-r--r-- | security/apparmor/policy_unpack.c | 4 | ||||
-rw-r--r-- | security/apparmor/policy_unpack_test.c | 607 |
8 files changed, 686 insertions, 49 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index a422a349f926..0fe336860773 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -68,3 +68,19 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES 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_KUNIT_TEST + bool "Build KUnit tests for policy_unpack.c" + depends on KUNIT=y && SECURITY_APPARMOR + help + This builds the AppArmor KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (http://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 09996f2552ee..280741fc0f5f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -623,7 +623,7 @@ static __poll_t ns_revision_poll(struct file *file, poll_table *pt) void __aa_bump_ns_revision(struct aa_ns *ns) { - ns->revision++; + WRITE_ONCE(ns->revision, ns->revision + 1); wake_up_interruptible(&ns->wait); } @@ -2573,16 +2573,18 @@ static const char *policy_get_link(struct dentry *dentry, { struct aa_ns *ns; struct path path; + int error; if (!dentry) return ERR_PTR(-ECHILD); + ns = aa_get_current_ns(); path.mnt = mntget(aafs_mnt); path.dentry = dget(ns_dir(ns)); - nd_jump_link(&path); + error = nd_jump_link(&path); aa_put_ns(ns); - return NULL; + return ERR_PTR(error); } static int policy_readlink(struct dentry *dentry, char __user *buffer, diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 9be7ccb8379e..6ceb74e0f789 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -317,6 +317,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, if (!bprm || !profile->xattr_count) return 0; + might_sleep(); /* transition from exec match to xattr set */ state = aa_dfa_null_transition(profile->xmatch, state); @@ -361,10 +362,11 @@ out: } /** - * __attach_match_ - find an attachment match + * find_attach - do attachment search for unconfined processes * @bprm - binprm structure of transitioning task - * @name - to match against (NOT NULL) + * @ns: the current namespace (NOT NULL) * @head - profile list to walk (NOT NULL) + * @name - to match against (NOT NULL) * @info - info message if there was an error (NOT NULL) * * Do a linear search on the profiles in the list. There is a matching @@ -374,12 +376,11 @@ out: * * Requires: @head not be shared or have appropriate locks held * - * Returns: profile or NULL if no match found + * Returns: label or NULL if no match found */ -static struct aa_profile *__attach_match(const struct linux_binprm *bprm, - const char *name, - struct list_head *head, - const char **info) +static struct aa_label *find_attach(const struct linux_binprm *bprm, + struct aa_ns *ns, struct list_head *head, + const char *name, const char **info) { int candidate_len = 0, candidate_xattrs = 0; bool conflict = false; @@ -388,6 +389,8 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, AA_BUG(!name); AA_BUG(!head); + rcu_read_lock(); +restart: list_for_each_entry_rcu(profile, head, base.list) { if (profile->label.flags & FLAG_NULL && &profile->label == ns_unconfined(profile->ns)) @@ -413,16 +416,32 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, perm = dfa_user_allow(profile->xmatch, state); /* any accepting state means a valid match. */ if (perm & MAY_EXEC) { - int ret; + int ret = 0; if (count < candidate_len) continue; - ret = aa_xattrs_match(bprm, profile, state); - /* Fail matching if the xattrs don't match */ - if (ret < 0) - continue; - + if (bprm && profile->xattr_count) { + long rev = READ_ONCE(ns->revision); + + if (!aa_get_profile_not0(profile)) + goto restart; + rcu_read_unlock(); + ret = aa_xattrs_match(bprm, profile, + state); + rcu_read_lock(); + aa_put_profile(profile); + if (rev != + READ_ONCE(ns->revision)) + /* policy changed */ + goto restart; + /* + * Fail matching if the xattrs don't + * match + */ + if (ret < 0) + continue; + } /* * TODO: allow for more flexible best match * @@ -445,43 +464,28 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, candidate_xattrs = ret; conflict = false; } - } else if (!strcmp(profile->base.name, name)) + } else if (!strcmp(profile->base.name, name)) { /* * old exact non-re match, without conditionals such * as xattrs. no more searching required */ - return profile; + candidate = profile; + goto out; + } } - if (conflict) { - *info = "conflicting profile attachments"; + if (!candidate || conflict) { + if (conflict) + *info = "conflicting profile attachments"; + rcu_read_unlock(); return NULL; } - return candidate; -} - -/** - * find_attach - do attachment search for unconfined processes - * @bprm - binprm structure of transitioning task - * @ns: the current namespace (NOT NULL) - * @list: list to search (NOT NULL) - * @name: the executable name to match against (NOT NULL) - * @info: info message if there was an error - * - * Returns: label or NULL if no match found - */ -static struct aa_label *find_attach(const struct linux_binprm *bprm, - struct aa_ns *ns, struct list_head *list, - const char *name, const char **info) -{ - struct aa_profile *profile; - - rcu_read_lock(); - profile = aa_get_profile(__attach_match(bprm, name, list, info)); +out: + candidate = aa_get_newest_profile(candidate); rcu_read_unlock(); - return profile ? &profile->label : NULL; + return &candidate->label; } static const char *next_name(int xtype, const char *name) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index fe2ebe5e865e..f1caf3674e1c 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -618,8 +618,7 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, fctx = file_ctx(file); rcu_read_lock(); - flabel = aa_get_newest_label(rcu_dereference(fctx->label)); - rcu_read_unlock(); + flabel = rcu_dereference(fctx->label); AA_BUG(!flabel); /* revalidate access, if task is unconfined, or the cached cred @@ -631,9 +630,13 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, */ denied = request & ~fctx->allow; if (unconfined(label) || unconfined(flabel) || - (!denied && aa_label_is_subset(flabel, label))) + (!denied && aa_label_is_subset(flabel, label))) { + rcu_read_unlock(); goto done; + } + flabel = aa_get_newest_label(flabel); + rcu_read_unlock(); /* TODO: label cross check */ if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) @@ -643,8 +646,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, else if (S_ISSOCK(file_inode(file)->i_mode)) error = __file_sock_perm(op, label, flabel, file, request, denied); -done: aa_put_label(flabel); + +done: return error; } diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 4ed6688f9d40..e0828ee7a345 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -442,7 +442,7 @@ int aa_bind_mount(struct aa_label *label, const struct path *path, buffer = aa_get_buffer(false); old_buffer = aa_get_buffer(false); error = -ENOMEM; - if (!buffer || old_buffer) + if (!buffer || !old_buffer) goto out; error = fn_for_each_confined(label, profile, diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 03104830c913..269f2f53c0b1 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1125,8 +1125,8 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj, if (!name) { /* remove namespace - can only happen if fqname[0] == ':' */ mutex_lock_nested(&ns->parent->lock, ns->level); - __aa_remove_ns(ns); __aa_bump_ns_revision(ns); + __aa_remove_ns(ns); mutex_unlock(&ns->parent->lock); } else { /* remove profile */ @@ -1138,9 +1138,9 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj, goto fail_ns_lock; } name = profile->base.hname; + __aa_bump_ns_revision(ns); __remove_profile(profile); __aa_labelset_update_subtree(ns); - __aa_bump_ns_revision(ns); mutex_unlock(&ns->lock); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 80364310fb1e..2d743c004bc4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1228,3 +1228,7 @@ fail: return error; } + +#ifdef CONFIG_SECURITY_APPARMOR_KUNIT_TEST +#include "policy_unpack_test.c" +#endif /* CONFIG_SECURITY_APPARMOR_KUNIT_TEST */ diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c new file mode 100644 index 000000000000..533137f45361 --- /dev/null +++ b/security/apparmor/policy_unpack_test.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for AppArmor's policy unpack. + */ + +#include <kunit/test.h> + +#include "include/policy.h" +#include "include/policy_unpack.h" + +#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 ((u32)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 ((u64)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 (ARRAY_SIZE(TEST_BLOB_DATA)) +#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 kunit *test, size_t buf_size) +{ + char *buf; + struct aa_ext *e; + + buf = kunit_kzalloc(test, buf_size, GFP_USER); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, buf); + + e = kunit_kmalloc(test, sizeof(*e), GFP_USER); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, e); + + e->start = buf; + e->end = e->start + buf_size; + e->pos = e->start; + + *buf = AA_NAME; + *(buf + 1) = strlen(TEST_STRING_NAME) + 1; + strcpy(buf + 3, TEST_STRING_NAME); + + buf = e->start + TEST_STRING_BUF_OFFSET; + *buf = AA_STRING; + *(buf + 1) = strlen(TEST_STRING_DATA) + 1; + strcpy(buf + 3, TEST_STRING_DATA); + + buf = e->start + TEST_NAMED_U32_BUF_OFFSET; + *buf = AA_NAME; + *(buf + 1) = strlen(TEST_U32_NAME) + 1; + strcpy(buf + 3, TEST_U32_NAME); + *(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; + *buf = AA_NAME; + *(buf + 1) = strlen(TEST_U64_NAME) + 1; + strcpy(buf + 3, TEST_U64_NAME); + *(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; + *buf = AA_NAME; + *(buf + 1) = strlen(TEST_BLOB_NAME) + 1; + strcpy(buf + 3, TEST_BLOB_NAME); + *(buf + 3 + strlen(TEST_BLOB_NAME) + 1) = AA_BLOB; + *(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; + *buf = AA_NAME; + *(buf + 1) = strlen(TEST_ARRAY_NAME) + 1; + strcpy(buf + 3, TEST_ARRAY_NAME); + *(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 kunit *test) +{ + size_t e_size = TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1; + struct policy_unpack_fixture *puf; + + puf = kunit_kmalloc(test, sizeof(*puf), GFP_USER); + KUNIT_EXPECT_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 policy_unpack_test_inbounds_when_inbounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + + KUNIT_EXPECT_TRUE(test, inbounds(puf->e, 0)); + KUNIT_EXPECT_TRUE(test, inbounds(puf->e, puf->e_size / 2)); + KUNIT_EXPECT_TRUE(test, inbounds(puf->e, puf->e_size)); +} + +static void policy_unpack_test_inbounds_when_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + + KUNIT_EXPECT_FALSE(test, inbounds(puf->e, puf->e_size + 1)); +} + +static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + u16 array_size; + + puf->e->pos += TEST_ARRAY_BUF_OFFSET; + + array_size = unpack_array(puf->e, NULL); + + KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); +} + +static void policy_unpack_test_unpack_array_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_ARRAY_NAME; + u16 array_size; + + puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET; + + array_size = unpack_array(puf->e, name); + + KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); +} + +static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_ARRAY_NAME; + u16 array_size; + + 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); + + KUNIT_EXPECT_EQ(test, array_size, (u16)0); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET); +} + +static void policy_unpack_test_unpack_blob_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *blob = NULL; + size_t size; + + puf->e->pos += TEST_BLOB_BUF_OFFSET; + size = unpack_blob(puf->e, &blob, NULL); + + KUNIT_ASSERT_EQ(test, size, TEST_BLOB_DATA_SIZE); + KUNIT_EXPECT_TRUE(test, + memcmp(blob, TEST_BLOB_DATA, TEST_BLOB_DATA_SIZE) == 0); +} + +static void policy_unpack_test_unpack_blob_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *blob = NULL; + size_t size; + + puf->e->pos += TEST_NAMED_BLOB_BUF_OFFSET; + size = unpack_blob(puf->e, &blob, TEST_BLOB_NAME); + + KUNIT_ASSERT_EQ(test, size, TEST_BLOB_DATA_SIZE); + KUNIT_EXPECT_TRUE(test, + memcmp(blob, TEST_BLOB_DATA, TEST_BLOB_DATA_SIZE) == 0); +} + +static void policy_unpack_test_unpack_blob_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *blob = NULL; + void *start; + int size; + + 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); + + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); +} + +static void policy_unpack_test_unpack_str_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char *string = NULL; + size_t size; + + puf->e->pos += TEST_STRING_BUF_OFFSET; + size = unpack_str(puf->e, &string, NULL); + + KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); + KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); +} + +static void policy_unpack_test_unpack_str_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char *string = NULL; + size_t size; + + size = unpack_str(puf->e, &string, TEST_STRING_NAME); + + KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); + KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); +} + +static void policy_unpack_test_unpack_str_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char *string = NULL; + void *start = puf->e->pos; + int size; + + puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET + + strlen(TEST_STRING_DATA) - 1; + + size = unpack_str(puf->e, &string, TEST_STRING_NAME); + + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); +} + +static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *string = NULL; + size_t size; + + puf->e->pos += TEST_STRING_BUF_OFFSET; + size = unpack_strdup(puf->e, &string, NULL); + + KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); + KUNIT_EXPECT_FALSE(test, + ((uintptr_t)puf->e->start <= (uintptr_t)string) + && ((uintptr_t)string <= (uintptr_t)puf->e->end)); + KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); +} + +static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *string = NULL; + size_t size; + + size = unpack_strdup(puf->e, &string, TEST_STRING_NAME); + + KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); + KUNIT_EXPECT_FALSE(test, + ((uintptr_t)puf->e->start <= (uintptr_t)string) + && ((uintptr_t)string <= (uintptr_t)puf->e->end)); + KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); +} + +static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + void *start = puf->e->pos; + char *string = NULL; + int size; + + puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET + + strlen(TEST_STRING_DATA) - 1; + + size = unpack_strdup(puf->e, &string, TEST_STRING_NAME); + + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_PTR_EQ(test, string, (char *)NULL); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); +} + +static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success; + + puf->e->pos += TEST_U32_BUF_OFFSET; + + success = unpack_nameX(puf->e, AA_U32, NULL); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U32_BUF_OFFSET + 1); +} + +static void policy_unpack_test_unpack_nameX_with_wrong_code(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success; + + puf->e->pos += TEST_U32_BUF_OFFSET; + + success = unpack_nameX(puf->e, AA_BLOB, NULL); + + KUNIT_EXPECT_FALSE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U32_BUF_OFFSET); +} + +static void policy_unpack_test_unpack_nameX_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_U32_NAME; + bool success; + + puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; + + success = unpack_nameX(puf->e, AA_U32, name); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U32_BUF_OFFSET + 1); +} + +static void policy_unpack_test_unpack_nameX_with_wrong_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + static const char name[] = "12345678"; + bool success; + + puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; + + success = unpack_nameX(puf->e, AA_U32, name); + + KUNIT_EXPECT_FALSE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_NAMED_U32_BUF_OFFSET); +} + +static void policy_unpack_test_unpack_u16_chunk_basic(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *chunk = NULL; + size_t size; + + 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); + + KUNIT_EXPECT_PTR_EQ(test, (void *)chunk, + puf->e->start + TEST_U16_OFFSET + 2); + KUNIT_EXPECT_EQ(test, size, (size_t)TEST_U16_DATA); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, (void *)(chunk + TEST_U16_DATA)); +} + +static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1( + struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *chunk = NULL; + size_t size; + + puf->e->pos = puf->e->end - 1; + + size = unpack_u16_chunk(puf->e, &chunk); + + KUNIT_EXPECT_EQ(test, size, (size_t)0); + KUNIT_EXPECT_PTR_EQ(test, chunk, (char *)NULL); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->end - 1); +} + +static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_2( + struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + char *chunk = NULL; + size_t size; + + 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); + + KUNIT_EXPECT_EQ(test, size, (size_t)0); + KUNIT_EXPECT_PTR_EQ(test, chunk, (char *)NULL); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_U16_OFFSET); +} + +static void policy_unpack_test_unpack_u32_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success; + u32 data; + + puf->e->pos += TEST_U32_BUF_OFFSET; + + success = unpack_u32(puf->e, &data, NULL); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_EQ(test, data, TEST_U32_DATA); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32) + 1); +} + +static void policy_unpack_test_unpack_u32_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_U32_NAME; + bool success; + u32 data; + + puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; + + success = unpack_u32(puf->e, &data, name); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_EQ(test, data, TEST_U32_DATA); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32) + 1); +} + +static void policy_unpack_test_unpack_u32_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_U32_NAME; + bool success; + u32 data; + + 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); + + KUNIT_EXPECT_FALSE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_NAMED_U32_BUF_OFFSET); +} + +static void policy_unpack_test_unpack_u64_with_null_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success; + u64 data; + + puf->e->pos += TEST_U64_BUF_OFFSET; + + success = unpack_u64(puf->e, &data, NULL); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_EQ(test, data, TEST_U64_DATA); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64) + 1); +} + +static void policy_unpack_test_unpack_u64_with_name(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_U64_NAME; + bool success; + u64 data; + + puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; + + success = unpack_u64(puf->e, &data, name); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_EQ(test, data, TEST_U64_DATA); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64) + 1); +} + +static void policy_unpack_test_unpack_u64_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + const char name[] = TEST_U64_NAME; + bool success; + u64 data; + + 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); + + KUNIT_EXPECT_FALSE(test, success); + KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, + puf->e->start + TEST_NAMED_U64_BUF_OFFSET); +} + +static void policy_unpack_test_unpack_X_code_match(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success = unpack_X(puf->e, AA_NAME); + + KUNIT_EXPECT_TRUE(test, success); + KUNIT_EXPECT_TRUE(test, puf->e->pos == puf->e->start + 1); +} + +static void policy_unpack_test_unpack_X_code_mismatch(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success = unpack_X(puf->e, AA_STRING); + + KUNIT_EXPECT_FALSE(test, success); + KUNIT_EXPECT_TRUE(test, puf->e->pos == puf->e->start); +} + +static void policy_unpack_test_unpack_X_out_of_bounds(struct kunit *test) +{ + struct policy_unpack_fixture *puf = test->priv; + bool success; + + puf->e->pos = puf->e->end; + success = unpack_X(puf->e, AA_NAME); + + KUNIT_EXPECT_FALSE(test, success); +} + +static struct kunit_case apparmor_policy_unpack_test_cases[] = { + KUNIT_CASE(policy_unpack_test_inbounds_when_inbounds), + KUNIT_CASE(policy_unpack_test_inbounds_when_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_array_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_array_with_name), + KUNIT_CASE(policy_unpack_test_unpack_array_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_blob_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_blob_with_name), + KUNIT_CASE(policy_unpack_test_unpack_blob_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_nameX_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_nameX_with_wrong_code), + KUNIT_CASE(policy_unpack_test_unpack_nameX_with_name), + KUNIT_CASE(policy_unpack_test_unpack_nameX_with_wrong_name), + KUNIT_CASE(policy_unpack_test_unpack_str_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_str_with_name), + KUNIT_CASE(policy_unpack_test_unpack_str_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_strdup_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_strdup_with_name), + KUNIT_CASE(policy_unpack_test_unpack_strdup_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_u16_chunk_basic), + KUNIT_CASE(policy_unpack_test_unpack_u16_chunk_out_of_bounds_1), + KUNIT_CASE(policy_unpack_test_unpack_u16_chunk_out_of_bounds_2), + KUNIT_CASE(policy_unpack_test_unpack_u32_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_u32_with_name), + KUNIT_CASE(policy_unpack_test_unpack_u32_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_u64_with_null_name), + KUNIT_CASE(policy_unpack_test_unpack_u64_with_name), + KUNIT_CASE(policy_unpack_test_unpack_u64_out_of_bounds), + KUNIT_CASE(policy_unpack_test_unpack_X_code_match), + KUNIT_CASE(policy_unpack_test_unpack_X_code_mismatch), + KUNIT_CASE(policy_unpack_test_unpack_X_out_of_bounds), + {}, +}; + +static struct kunit_suite apparmor_policy_unpack_test_module = { + .name = "apparmor_policy_unpack", + .init = policy_unpack_test_init, + .test_cases = apparmor_policy_unpack_test_cases, +}; + +kunit_test_suite(apparmor_policy_unpack_test_module); |