]> git.saurik.com Git - apple/xnu.git/blobdiff - san/kasan_dynamic_blacklist.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / san / kasan_dynamic_blacklist.c
index 97fc3a5612bff5634430d4f14be4494ce8d2821d..cb661abd0a56baa10bab246475ee9a04d2cf87d5 100644 (file)
 struct blacklist_entry {
        const char *kext_name;
        const char *func_name;
-       const unsigned type_mask;
+       access_t type_mask;
 
        /* internal */
        uint64_t count;
 };
 
 #include "kasan_blacklist_dynamic.h"
-static const size_t blacklist_entries = sizeof(blacklist)/sizeof(blacklist[0]);
+/* defines 'blacklist' and 'blacklist_entries' */
 
 decl_simple_lock_data(static, _dybl_lock);
+static access_t blacklisted_types; /* bitmap of access types with blacklist entries */
 
 static void
 dybl_lock(boolean_t *b)
 {
        *b = ml_set_interrupts_enabled(false);
-       simple_lock(&_dybl_lock);
+       simple_lock(&_dybl_lock, LCK_GRP_NULL);
 }
 
 static void
@@ -313,7 +314,6 @@ addr_to_func(uintptr_t addr, const kernel_mach_header_t *mh)
         * iterate the symbols, looking for the closest one to `addr'
         */
        for (i = 0; i < (int)st->nsyms; i++) {
-
                uint8_t n_type = syms[i].n_type;
                const char *name = strings + syms[i].n_un.n_strx;
 
@@ -349,19 +349,28 @@ addr_to_func(uintptr_t addr, const kernel_mach_header_t *mh)
        return cur_name;
 }
 
-bool NOINLINE
-kasan_is_blacklisted(unsigned mask)
+bool OS_NOINLINE
+kasan_is_blacklisted(access_t type)
 {
        uint32_t nframes = 0;
        uintptr_t frames[MAX_FRAMES];
        uintptr_t *bt = frames;
-       nframes = backtrace(bt, MAX_FRAMES);
+
+       assert(__builtin_popcount(type) == 1);
+
+       if ((type & blacklisted_types) == 0) {
+               /* early exit for types with no blacklist entries */
+               return false;
+       }
+
+       nframes = backtrace_frame(bt, MAX_FRAMES, __builtin_frame_address(0),
+           NULL);
        boolean_t flag;
 
-       if (nframes >= 2) {
-               /* ignore self and direct caller */
-               nframes -= 2;
-               bt += 2;
+       if (nframes >= 1) {
+               /* ignore direct caller */
+               nframes -= 1;
+               bt += 1;
        }
 
        struct blacklist_hash_entry *blhe = NULL;
@@ -372,7 +381,7 @@ kasan_is_blacklisted(unsigned mask)
        for (uint32_t i = 0; i < nframes; i++) {
                blhe = blacklist_hash_lookup(bt[i]);
                if (blhe) {
-                       if ((blhe->ble->type_mask & mask) != mask) {
+                       if ((blhe->ble->type_mask & type) != type) {
                                /* wrong type */
                                continue;
                        }
@@ -381,7 +390,7 @@ kasan_is_blacklisted(unsigned mask)
                        blhe->count++;
                        blhe->ble->count++;
                        // printf("KASan: blacklist cache hit (%s:%s [0x%lx] 0x%x)\n",
-                       //              ble->kext_name ?: "" , ble->func_name ?: "", VM_KERNEL_UNSLIDE(bt[i]), mask);
+                       //              ble->kext_name ?: "" , ble->func_name ?: "", VM_KERNEL_UNSLIDE(bt[i]), mask);
                        dybl_unlock(flag);
                        return true;
                }
@@ -389,7 +398,6 @@ kasan_is_blacklisted(unsigned mask)
 
        /* no hits - slowpath */
        for (uint32_t i = 0; i < nframes; i++) {
-
                const char *kextname = NULL;
                const char *funcname = NULL;
 
@@ -419,7 +427,7 @@ kasan_is_blacklisted(unsigned mask)
                        struct blacklist_entry *ble = &blacklist[j];
                        uint64_t count;
 
-                       if ((ble->type_mask & mask) != mask) {
+                       if ((ble->type_mask & type) != type) {
                                /* wrong type */
                                continue;
                        }
@@ -443,7 +451,7 @@ kasan_is_blacklisted(unsigned mask)
 
                        if (count == 0) {
                                printf("KASan: ignoring blacklisted violation (%s:%s [0x%lx] %d 0x%x)\n",
-                                               kextname, funcname, VM_KERNEL_UNSLIDE(bt[i]), i, mask);
+                                   kextname, funcname, VM_KERNEL_UNSLIDE(bt[i]), i, type);
                        }
 
                        return true;
@@ -454,11 +462,124 @@ kasan_is_blacklisted(unsigned mask)
        return false;
 }
 
+static void
+add_blacklist_entry(const char *kext, const char *func, access_t type)
+{
+       assert(kext || func);
+       struct blacklist_entry *ble = &blacklist[blacklist_entries++];
+
+       if (blacklist_entries > blacklist_max_entries) {
+               panic("KASan: dynamic blacklist entries exhausted\n");
+       }
+
+       if (kext) {
+               size_t sz = __nosan_strlen(kext) + 1;
+               if (sz > 1) {
+                       char *s = kalloc(sz);
+                       __nosan_strlcpy(s, kext, sz);
+                       ble->kext_name = s;
+               }
+       }
+
+       if (func) {
+               size_t sz = __nosan_strlen(func) + 1;
+               if (sz > 1) {
+                       char *s = kalloc(sz);
+                       __nosan_strlcpy(s, func, sz);
+                       ble->func_name = s;
+               }
+       }
+
+       ble->type_mask = type;
+}
+
+#define TS(x) { .type = TYPE_##x, .str = #x }
+
+static const struct {
+       const access_t type;
+       const char * const str;
+} typemap[] = {
+       TS(LOAD),
+       TS(STORE),
+       TS(MEMR),
+       TS(MEMW),
+       TS(STRR),
+       TS(STRW),
+       TS(KFREE),
+       TS(ZFREE),
+       TS(FSFREE),
+       TS(UAF),
+       TS(POISON_GLOBAL),
+       TS(POISON_HEAP),
+       TS(MEM),
+       TS(STR),
+       TS(READ),
+       TS(WRITE),
+       TS(RW),
+       TS(FREE),
+       TS(NORMAL),
+       TS(DYNAMIC),
+       TS(POISON),
+       TS(ALL),
+
+       /* convenience aliases */
+       { .type = TYPE_POISON_GLOBAL, .str = "GLOB" },
+       { .type = TYPE_POISON_HEAP, .str = "HEAP" },
+};
+static size_t typemap_sz = sizeof(typemap) / sizeof(typemap[0]);
+
+static inline access_t
+map_type(const char *str)
+{
+       if (strlen(str) == 0) {
+               return TYPE_NORMAL;
+       }
+
+       /* convert type string to integer ID */
+       for (size_t i = 0; i < typemap_sz; i++) {
+               if (strcasecmp(str, typemap[i].str) == 0) {
+                       return typemap[i].type;
+               }
+       }
+
+       printf("KASan: unknown blacklist type `%s', assuming `normal'\n", str);
+       return TYPE_NORMAL;
+}
+
 void
 kasan_init_dybl(void)
 {
        simple_lock_init(&_dybl_lock, 0);
 
+       /*
+        * dynamic blacklist entries via boot-arg. Syntax is:
+        *  kasan.bl=kext1:func1:type1,kext2:func2:type2,...
+        */
+       char buf[256] = {};
+       char *bufp = buf;
+       if (PE_parse_boot_arg_str("kasan.bl", bufp, sizeof(buf))) {
+               char *kext;
+               while ((kext = strsep(&bufp, ",")) != NULL) {
+                       access_t type = TYPE_NORMAL;
+                       char *func = strchr(kext, ':');
+                       if (func) {
+                               *func++ = 0;
+                       }
+                       char *typestr = strchr(func, ':');
+                       if (typestr) {
+                               *typestr++ = 0;
+                               type = map_type(typestr);
+                       }
+                       add_blacklist_entry(kext, func, type);
+               }
+       }
+
+       /* collect bitmask of blacklisted types */
+       for (size_t j = 0; j < blacklist_entries; j++) {
+               struct blacklist_entry *ble = &blacklist[j];
+               blacklisted_types |= ble->type_mask;
+       }
+
        /* add the fake kernel kext */
        kasan_dybl_load_kext((uintptr_t)&_mh_execute_header, "__kernel__");
 }
@@ -466,7 +587,7 @@ kasan_init_dybl(void)
 #else /* KASAN_DYNAMIC_BLACKLIST */
 
 bool
-kasan_is_blacklisted(unsigned __unused mask)
+kasan_is_blacklisted(access_t __unused type)
 {
        return false;
 }