]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/arm64/kernel/traps.c
Merge branch 'for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
[linux.git] / arch / arm64 / kernel / traps.c
index 5f4d9acb32f50ea3fb13c35c3cc5cd5b5514a548..cdc71cf70aad4027f9638ebae8454e719ef3900a 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/sizes.h>
 #include <linux/syscalls.h>
 #include <linux/mm_types.h>
+#include <linux/kasan.h>
 
 #include <asm/atomic.h>
 #include <asm/bug.h>
@@ -969,6 +970,58 @@ static struct break_hook bug_break_hook = {
        .fn = bug_handler,
 };
 
+#ifdef CONFIG_KASAN_SW_TAGS
+
+#define KASAN_ESR_RECOVER      0x20
+#define KASAN_ESR_WRITE        0x10
+#define KASAN_ESR_SIZE_MASK    0x0f
+#define KASAN_ESR_SIZE(esr)    (1 << ((esr) & KASAN_ESR_SIZE_MASK))
+
+static int kasan_handler(struct pt_regs *regs, unsigned int esr)
+{
+       bool recover = esr & KASAN_ESR_RECOVER;
+       bool write = esr & KASAN_ESR_WRITE;
+       size_t size = KASAN_ESR_SIZE(esr);
+       u64 addr = regs->regs[0];
+       u64 pc = regs->pc;
+
+       if (user_mode(regs))
+               return DBG_HOOK_ERROR;
+
+       kasan_report(addr, size, write, pc);
+
+       /*
+        * The instrumentation allows to control whether we can proceed after
+        * a crash was detected. This is done by passing the -recover flag to
+        * the compiler. Disabling recovery allows to generate more compact
+        * code.
+        *
+        * Unfortunately disabling recovery doesn't work for the kernel right
+        * now. KASAN reporting is disabled in some contexts (for example when
+        * the allocator accesses slab object metadata; this is controlled by
+        * current->kasan_depth). All these accesses are detected by the tool,
+        * even though the reports for them are not printed.
+        *
+        * This is something that might be fixed at some point in the future.
+        */
+       if (!recover)
+               die("Oops - KASAN", regs, 0);
+
+       /* If thread survives, skip over the brk instruction and continue: */
+       arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
+       return DBG_HOOK_HANDLED;
+}
+
+#define KASAN_ESR_VAL (0xf2000000 | KASAN_BRK_IMM)
+#define KASAN_ESR_MASK 0xffffff00
+
+static struct break_hook kasan_break_hook = {
+       .esr_val = KASAN_ESR_VAL,
+       .esr_mask = KASAN_ESR_MASK,
+       .fn = kasan_handler,
+};
+#endif
+
 /*
  * Initial handler for AArch64 BRK exceptions
  * This handler only used until debug_traps_init().
@@ -976,6 +1029,10 @@ static struct break_hook bug_break_hook = {
 int __init early_brk64(unsigned long addr, unsigned int esr,
                struct pt_regs *regs)
 {
+#ifdef CONFIG_KASAN_SW_TAGS
+       if ((esr & KASAN_ESR_MASK) == KASAN_ESR_VAL)
+               return kasan_handler(regs, esr) != DBG_HOOK_HANDLED;
+#endif
        return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
 }
 
@@ -983,4 +1040,7 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
 void __init trap_init(void)
 {
        register_break_hook(&bug_break_hook);
+#ifdef CONFIG_KASAN_SW_TAGS
+       register_break_hook(&kasan_break_hook);
+#endif
 }