]> git.saurik.com Git - apple/xnu.git/blobdiff - san/kasan-test.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / san / kasan-test.c
index 6dc379c1a0b4d9b2f718cb26dc6bb191a350f970..66c16c391898056342a67e05f4ac15d7007e279d 100644 (file)
@@ -34,6 +34,7 @@
 #include <kern/kalloc.h>
 #include <kern/simple_lock.h>
 #include <kern/debug.h>
+#include <kern/thread.h>
 #include <mach/mach_vm.h>
 #include <mach/vm_param.h>
 #include <libkern/libkern.h>
@@ -75,11 +76,12 @@ struct kasan_test {
 #define TEST_START(t)     do { t->result = 1; TEST_BARRIER(); } while (0)
 #define TEST_FAULT(t)     do { TEST_BARRIER(); t->result = 0; TEST_BARRIER(); } while (0)
 #define TEST_NOFAULT(t)   do { TEST_BARRIER(); t->result = 1; TEST_BARRIER(); } while (0)
-#define TEST_DONE(t,res)  do { t->result = (res); kasan_handle_test(); } while (0)
-#define DECLARE_TEST(f,s)    { .func = f, .name = s }
-#define DECLARE_TEST3(f,c,s) { .func = f, .cleanup = c, .name = s }
+#define TEST_DONE(t, res)  do { t->result = (res); kasan_handle_test(); } while (0)
+#define DECLARE_TEST(f, s)    { .func = f, .name = s }
+#define DECLARE_TEST3(f, c, s) { .func = f, .cleanup = c, .name = s }
 
-static void heap_cleanup(struct kasan_test *t)
+static void
+heap_cleanup(struct kasan_test *t)
 {
        if (t->data) {
                kfree(t->data, t->datasz);
@@ -87,7 +89,8 @@ static void heap_cleanup(struct kasan_test *t)
        }
 }
 
-static int test_global_overflow(struct kasan_test __unused *t)
+static int
+test_global_overflow(struct kasan_test __unused *t)
 {
        int i;
        /* rookie error */
@@ -97,7 +100,8 @@ static int test_global_overflow(struct kasan_test __unused *t)
        return 0;
 }
 
-static int test_heap_underflow(struct kasan_test __unused *t)
+static int
+test_heap_underflow(struct kasan_test __unused *t)
 {
        uint8_t *x = kalloc(BUFSZ);
        if (!x) {
@@ -109,7 +113,8 @@ static int test_heap_underflow(struct kasan_test __unused *t)
        return 0;
 }
 
-static int test_heap_overflow(struct kasan_test __unused *t)
+static int
+test_heap_overflow(struct kasan_test __unused *t)
 {
        uint8_t *x = kalloc(BUFSZ);
        if (!x) {
@@ -121,7 +126,8 @@ static int test_heap_overflow(struct kasan_test __unused *t)
        return 0;
 }
 
-static int test_heap_uaf(struct kasan_test __unused *t)
+static int
+test_heap_uaf(struct kasan_test __unused *t)
 {
        uint8_t *x = kalloc(LBUFSZ);
        if (!x) {
@@ -132,14 +138,17 @@ static int test_heap_uaf(struct kasan_test __unused *t)
        return 0;
 }
 
-static int test_heap_inval_free(struct kasan_test __unused *t)
+static int
+test_heap_inval_free(struct kasan_test __unused *t)
 {
        int x;
-       kfree(&x, BUFSZ);
+       int *ptr = &x;
+       kfree(ptr, BUFSZ);
        return 0;
 }
 
-static int test_heap_double_free(struct kasan_test *t)
+static int
+test_heap_double_free(struct kasan_test *t)
 {
        TEST_START(t);
 
@@ -155,7 +164,8 @@ static int test_heap_double_free(struct kasan_test *t)
        return 0;
 }
 
-static int test_heap_small_free(struct kasan_test *t)
+static int
+test_heap_small_free(struct kasan_test *t)
 {
        TEST_START(t);
 
@@ -167,18 +177,19 @@ static int test_heap_small_free(struct kasan_test *t)
        t->data = x;
 
        TEST_FAULT(t);
-       kfree(x, BUFSZ-2);
+       kfree(x, BUFSZ - 2);
        t->data = NULL;
        t->datasz = 0;
 
        return 0;
 }
 
-static int test_stack_overflow(struct kasan_test *t)
+static int
+test_stack_overflow(struct kasan_test *t)
 {
        TEST_START(t);
 
-       int i;
+       uint8_t i;
        volatile uint8_t a[STACK_ARRAY_SZ];
 
        for (i = 0; i < STACK_ARRAY_SZ; i++) {
@@ -194,7 +205,8 @@ static int test_stack_overflow(struct kasan_test *t)
        return !(a[0] == 0);
 }
 
-static int test_stack_underflow(struct kasan_test *t)
+static int
+test_stack_underflow(struct kasan_test *t)
 {
        TEST_START(t);
 
@@ -205,9 +217,9 @@ static int test_stack_underflow(struct kasan_test *t)
 
        /* generate a negative index without the compiler noticing */
 #if __x86_64__
-       __asm__ __volatile__("movq $-1, %0" : "=r"(idx) :: "memory");
+       __asm__ __volatile__ ("movq $-1, %0" : "=r"(idx) :: "memory");
 #else
-       __asm__ __volatile__("mov %0, #-1" : "=r"(idx) :: "memory");
+       __asm__ __volatile__ ("mov %0, #-1" : "=r"(idx) :: "memory");
 #endif
 
        TEST_FAULT(t);
@@ -215,10 +227,11 @@ static int test_stack_underflow(struct kasan_test *t)
        TEST_NOFAULT(t);
 
        TEST_BARRIER();
-       return (a[0] == 0);
+       return a[0] == 0;
 }
 
-static int test_memcpy(struct kasan_test *t)
+static int
+test_memcpy(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t a1[STACK_ARRAY_SZ];
@@ -231,13 +244,14 @@ static int test_memcpy(struct kasan_test *t)
 
        /* should fail */
        TEST_FAULT(t);
-       memcpy(a2, a1, STACK_ARRAY_SZ+1);
+       memcpy(a2, a1, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_memmove(struct kasan_test *t)
+static int
+test_memmove(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t a1[STACK_ARRAY_SZ];
@@ -250,13 +264,14 @@ static int test_memmove(struct kasan_test *t)
 
        /* should fail */
        TEST_FAULT(t);
-       memmove(a2, a1, STACK_ARRAY_SZ+1);
+       memmove(a2, a1, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_bcopy(struct kasan_test *t)
+static int
+test_bcopy(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t a1[STACK_ARRAY_SZ];
@@ -269,13 +284,14 @@ static int test_bcopy(struct kasan_test *t)
 
        /* should fail */
        TEST_FAULT(t);
-       bcopy(a2, a1, STACK_ARRAY_SZ+1);
+       bcopy(a2, a1, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_memset(struct kasan_test *t)
+static int
+test_memset(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t a1[STACK_ARRAY_SZ];
@@ -287,67 +303,74 @@ static int test_memset(struct kasan_test *t)
 
        /* should fail */
        TEST_FAULT(t);
-       memset(a1, 'f', STACK_ARRAY_SZ+1);
+       memset(a1, 'f', STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_memcmp(struct kasan_test *t)
+static int
+test_memcmp(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t *a1;
        uint8_t *a2;
 
        a1 = kalloc(STACK_ARRAY_SZ);
-       if (!a1)
+       if (!a1) {
                return 1;
-       a2 = kalloc(STACK_ARRAY_SZ+1);
-       if (!a2)
+       }
+       a2 = kalloc(STACK_ARRAY_SZ + 1);
+       if (!a2) {
                return 1;
+       }
 
        /* should work */
        memcmp(a1, a2, STACK_ARRAY_SZ);
-       memcmp(a1, a2+1, STACK_ARRAY_SZ);
+       memcmp(a1, a2 + 1, STACK_ARRAY_SZ);
 
        TEST_BARRIER();
 
        /* should fail */
        TEST_FAULT(t);
-       memcmp(a1, a2, STACK_ARRAY_SZ+1);
+       memcmp(a1, a2, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_bcmp(struct kasan_test *t)
+static int
+test_bcmp(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t *a1;
        uint8_t *a2;
 
        a1 = kalloc(STACK_ARRAY_SZ);
-       if (!a1)
+       if (!a1) {
                return 1;
-       a2 = kalloc(STACK_ARRAY_SZ+1);
-       if (!a2)
+       }
+       a2 = kalloc(STACK_ARRAY_SZ + 1);
+       if (!a2) {
                return 1;
+       }
 
        /* should work */
        bcmp(a1, a2, STACK_ARRAY_SZ);
-       bcmp(a1, a2+1, STACK_ARRAY_SZ);
+       bcmp(a1, a2 + 1, STACK_ARRAY_SZ);
 
        TEST_BARRIER();
 
        /* should fail */
        TEST_FAULT(t);
-       bcmp(a1, a2, STACK_ARRAY_SZ+1);
+       bcmp(a1, a2, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_bzero(struct kasan_test *t)
+static int
+test_bzero(struct kasan_test *t)
 {
        TEST_START(t);
        uint8_t a1[STACK_ARRAY_SZ];
@@ -359,13 +382,14 @@ static int test_bzero(struct kasan_test *t)
 
        /* should fail */
        TEST_FAULT(t);
-       bzero(a1, STACK_ARRAY_SZ+1);
+       bzero(a1, STACK_ARRAY_SZ + 1);
        TEST_NOFAULT(t);
 
        return 0;
 }
 
-static int test_strlcpy(struct kasan_test *t)
+static int
+test_strlcpy(struct kasan_test *t)
 {
        TEST_START(t);
        char a1[8];
@@ -381,7 +405,8 @@ static int test_strlcpy(struct kasan_test *t)
        return 0;
 }
 
-static int test_strncpy(struct kasan_test *t)
+static int
+test_strncpy(struct kasan_test *t)
 {
        TEST_START(t);
        char a1[9];
@@ -397,7 +422,8 @@ static int test_strncpy(struct kasan_test *t)
        return a1[0] != 'l';
 }
 
-static int test_strlcat(struct kasan_test *t)
+static int
+test_strlcat(struct kasan_test *t)
 {
        TEST_START(t);
        char a1[9] = {};
@@ -417,7 +443,8 @@ static int test_strlcat(struct kasan_test *t)
        return a1[0] != 'l';
 }
 
-static int test_strncat(struct kasan_test *t)
+static int
+test_strncat(struct kasan_test *t)
 {
        TEST_START(t);
        char a1[9] = {};
@@ -434,13 +461,13 @@ static int test_strncat(struct kasan_test *t)
 }
 
 /* we ignore the top *two* frames in backtrace - so add an extra one */
-static int __attribute__((noinline))
+static int OS_NOINLINE
 test_blacklist_helper(void)
 {
        return kasan_is_blacklisted(TYPE_TEST);
 }
 
-static int __attribute__((noinline))
+static int OS_NOINLINE
 test_blacklist(struct kasan_test *t)
 {
        TEST_START(t);
@@ -449,7 +476,7 @@ test_blacklist(struct kasan_test *t)
        return 0;
 }
 
-static int __attribute__((noinline))
+static int OS_NOINLINE
 test_blacklist_str(struct kasan_test *t)
 {
        TEST_START(t);
@@ -462,26 +489,74 @@ test_blacklist_str(struct kasan_test *t)
 }
 
 #if 0
-static int test_strnlen(struct kasan_test *t)
+static int
+test_strnlen(struct kasan_test *t)
 {
        TEST_START(t);
        const char *a1 = "abcdef";
 
        /* should not fault */
-       if (strnlen(a1, 6) != 6)
+       if (strnlen(a1, 6) != 6) {
                return 1;
-       if (strnlen(a1, 7) != 6)
+       }
+       if (strnlen(a1, 7) != 6) {
                return 1;
+       }
 
        TEST_FAULT(t);
-       if (strnlen(a1, 8) != 6)
+       if (strnlen(a1, 8) != 6) {
                return 1;
+       }
        TEST_NOFAULT(t);
 
        return a1[0] != 'a';
 }
 #endif
 
+static void OS_NOINLINE
+force_fakestack(char *x)
+{
+       __asm__ __volatile__ ("" :: "r" (x) : "memory");
+}
+
+OS_NOINLINE
+static int
+test_fakestack_helper(struct kasan_test *t, char *x)
+{
+       TEST_START(t);
+
+       x[0] = 0x55;
+
+       /* ensure that 'x' is on the fakestack */
+       uintptr_t base = dtrace_get_kernel_stack(current_thread());
+       uintptr_t p = (uintptr_t)x;
+       if (p >= base && p < base + kernel_stack_size) {
+               return 1;
+       }
+
+       __asan_handle_no_return();
+
+       /* x better still be accessible */
+       TEST_NOFAULT(t);
+       if (x[0] != 0x55) {
+               TEST_DONE(t, 1);
+       }
+
+       TEST_DONE(t, 0);
+       return 0;
+}
+
+static int
+test_fakestack(struct kasan_test *t)
+{
+       char x[8];
+       if (!fakestack_enabled) {
+               return 1;
+       }
+       force_fakestack(x);
+       return test_fakestack_helper(t, x);
+}
+
 int *uaf_ptr;
 static int * NOINLINE
 stack_uaf_helper(void)
@@ -491,7 +566,8 @@ stack_uaf_helper(void)
        return uaf_ptr;
 }
 
-static int test_stack_uaf(struct kasan_test __unused *t)
+static int
+test_stack_uaf(struct kasan_test __unused *t)
 {
        int *x = stack_uaf_helper();
        *x = 0xb4d;
@@ -502,31 +578,32 @@ static int test_stack_uaf(struct kasan_test __unused *t)
 static struct kasan_test xnu_tests[] = {
        DECLARE_TEST(NULL, NULL),
        DECLARE_TEST(test_global_overflow, "Global overflow"),
-       DECLARE_TEST3(test_heap_underflow,  heap_cleanup, "Heap underflow"),
-       DECLARE_TEST3(test_heap_overflow,   heap_cleanup, "Heap overflow"),
-       DECLARE_TEST(test_heap_uaf,        "Heap use-after-free"),
+       DECLARE_TEST3(test_heap_underflow, heap_cleanup, "Heap underflow"),
+       DECLARE_TEST3(test_heap_overflow, heap_cleanup, "Heap overflow"),
+       DECLARE_TEST(test_heap_uaf, "Heap use-after-free"),
        DECLARE_TEST(test_heap_inval_free, "Heap invalid free"),
-       DECLARE_TEST(test_heap_double_free,"Heap double free"),
+       DECLARE_TEST(test_heap_double_free, "Heap double free"),
        DECLARE_TEST3(test_heap_small_free, heap_cleanup, "Heap small free"),
-       DECLARE_TEST(test_stack_overflow,  "Stack overflow"),
+       DECLARE_TEST(test_stack_overflow, "Stack overflow"),
        DECLARE_TEST(test_stack_underflow, "Stack underflow"),
-       DECLARE_TEST(test_stack_uaf,       "Stack use-after-return"),
-       DECLARE_TEST(test_memcpy,          "memcpy"),
-       DECLARE_TEST(test_memmove,         "memmmove"),
-       DECLARE_TEST(test_bcopy,           "bcopy"),
-       DECLARE_TEST(test_memset,          "memset"),
-       DECLARE_TEST(test_memcmp,          "memcmp"),
-       DECLARE_TEST(test_bcmp,            "bcmp"),
-       DECLARE_TEST(test_bzero,           "bzero"),
-       DECLARE_TEST(test_strlcpy,         "strlcpy"),
-       DECLARE_TEST(test_strlcat,         "strlcat"),
-       DECLARE_TEST(test_strncpy,         "strncpy"),
-       DECLARE_TEST(test_strncat,         "strncat"),
-       DECLARE_TEST(test_blacklist,       "blacklist"),
-       DECLARE_TEST(test_blacklist_str,   "blacklist_str"),
+       DECLARE_TEST(test_stack_uaf, "Stack use-after-return"),
+       DECLARE_TEST(test_memcpy, "memcpy"),
+       DECLARE_TEST(test_memmove, "memmmove"),
+       DECLARE_TEST(test_bcopy, "bcopy"),
+       DECLARE_TEST(test_memset, "memset"),
+       DECLARE_TEST(test_memcmp, "memcmp"),
+       DECLARE_TEST(test_bcmp, "bcmp"),
+       DECLARE_TEST(test_bzero, "bzero"),
+       DECLARE_TEST(test_strlcpy, "strlcpy"),
+       DECLARE_TEST(test_strlcat, "strlcat"),
+       DECLARE_TEST(test_strncpy, "strncpy"),
+       DECLARE_TEST(test_strncat, "strncat"),
+       DECLARE_TEST(test_blacklist, "blacklist"),
+       DECLARE_TEST(test_blacklist_str, "blacklist_str"),
+       DECLARE_TEST(test_fakestack, "fakestack"),
        // DECLARE_TEST(test_strnlen,         "strnlen"),
 };
-static int num_xnutests = sizeof(xnu_tests)/sizeof(xnu_tests[0]);
+static int num_xnutests = sizeof(xnu_tests) / sizeof(xnu_tests[0]);
 
 static int
 kasan_run_test(struct kasan_test *test_list, int testno, int fail)
@@ -557,11 +634,6 @@ kasan_run_test(struct kasan_test *test_list, int testno, int fail)
                        status = TEST_FAIL_NOFAULT;
                }
        } else {
-               /* Triggering a KASan violation will return here by longjmp, bypassing
-                * stack unpoisoning, so do it here explicitly. We just hope that
-                * fakestack free will happen later... */
-               kasan_unpoison_curstack(true);
-
                if (t->result) {
                        /* faulted, but at the wrong place */
                        printf("KASan: test.%02d FAIL %d (%s)\n", testno, t->result, t->name);
@@ -588,7 +660,7 @@ kasan_test(int testno, int fail)
 
        if (testno == -1) {
                /* shorthand for all tests */
-               testno = (1U << (num_xnutests-1)) - 1;
+               testno = (1U << (num_xnutests - 1)) - 1;
        }
 
        while (testno) {