diff options
-rw-r--r-- | arch/arm64/include/asm/brk-imm.h | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/traps.c | 60 | ||||
-rw-r--r-- | include/linux/kasan.h | 3 |
3 files changed, 65 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/brk-imm.h b/arch/arm64/include/asm/brk-imm.h index ed693c5bcec0..2945fe6cd863 100644 --- a/arch/arm64/include/asm/brk-imm.h +++ b/arch/arm64/include/asm/brk-imm.h | |||
@@ -16,10 +16,12 @@ | |||
16 | * 0x400: for dynamic BRK instruction | 16 | * 0x400: for dynamic BRK instruction |
17 | * 0x401: for compile time BRK instruction | 17 | * 0x401: for compile time BRK instruction |
18 | * 0x800: kernel-mode BUG() and WARN() traps | 18 | * 0x800: kernel-mode BUG() and WARN() traps |
19 | * 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff) | ||
19 | */ | 20 | */ |
20 | #define FAULT_BRK_IMM 0x100 | 21 | #define FAULT_BRK_IMM 0x100 |
21 | #define KGDB_DYN_DBG_BRK_IMM 0x400 | 22 | #define KGDB_DYN_DBG_BRK_IMM 0x400 |
22 | #define KGDB_COMPILED_DBG_BRK_IMM 0x401 | 23 | #define KGDB_COMPILED_DBG_BRK_IMM 0x401 |
23 | #define BUG_BRK_IMM 0x800 | 24 | #define BUG_BRK_IMM 0x800 |
25 | #define KASAN_BRK_IMM 0x900 | ||
24 | 26 | ||
25 | #endif | 27 | #endif |
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 5f4d9acb32f5..cdc71cf70aad 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/sizes.h> | 35 | #include <linux/sizes.h> |
36 | #include <linux/syscalls.h> | 36 | #include <linux/syscalls.h> |
37 | #include <linux/mm_types.h> | 37 | #include <linux/mm_types.h> |
38 | #include <linux/kasan.h> | ||
38 | 39 | ||
39 | #include <asm/atomic.h> | 40 | #include <asm/atomic.h> |
40 | #include <asm/bug.h> | 41 | #include <asm/bug.h> |
@@ -969,6 +970,58 @@ static struct break_hook bug_break_hook = { | |||
969 | .fn = bug_handler, | 970 | .fn = bug_handler, |
970 | }; | 971 | }; |
971 | 972 | ||
973 | #ifdef CONFIG_KASAN_SW_TAGS | ||
974 | |||
975 | #define KASAN_ESR_RECOVER 0x20 | ||
976 | #define KASAN_ESR_WRITE 0x10 | ||
977 | #define KASAN_ESR_SIZE_MASK 0x0f | ||
978 | #define KASAN_ESR_SIZE(esr) (1 << ((esr) & KASAN_ESR_SIZE_MASK)) | ||
979 | |||
980 | static int kasan_handler(struct pt_regs *regs, unsigned int esr) | ||
981 | { | ||
982 | bool recover = esr & KASAN_ESR_RECOVER; | ||
983 | bool write = esr & KASAN_ESR_WRITE; | ||
984 | size_t size = KASAN_ESR_SIZE(esr); | ||
985 | u64 addr = regs->regs[0]; | ||
986 | u64 pc = regs->pc; | ||
987 | |||
988 | if (user_mode(regs)) | ||
989 | return DBG_HOOK_ERROR; | ||
990 | |||
991 | kasan_report(addr, size, write, pc); | ||
992 | |||
993 | /* | ||
994 | * The instrumentation allows to control whether we can proceed after | ||
995 | * a crash was detected. This is done by passing the -recover flag to | ||
996 | * the compiler. Disabling recovery allows to generate more compact | ||
997 | * code. | ||
998 | * | ||
999 | * Unfortunately disabling recovery doesn't work for the kernel right | ||
1000 | * now. KASAN reporting is disabled in some contexts (for example when | ||
1001 | * the allocator accesses slab object metadata; this is controlled by | ||
1002 | * current->kasan_depth). All these accesses are detected by the tool, | ||
1003 | * even though the reports for them are not printed. | ||
1004 | * | ||
1005 | * This is something that might be fixed at some point in the future. | ||
1006 | */ | ||
1007 | if (!recover) | ||
1008 | die("Oops - KASAN", regs, 0); | ||
1009 | |||
1010 | /* If thread survives, skip over the brk instruction and continue: */ | ||
1011 | arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); | ||
1012 | return DBG_HOOK_HANDLED; | ||
1013 | } | ||
1014 | |||
1015 | #define KASAN_ESR_VAL (0xf2000000 | KASAN_BRK_IMM) | ||
1016 | #define KASAN_ESR_MASK 0xffffff00 | ||
1017 | |||
1018 | static struct break_hook kasan_break_hook = { | ||
1019 | .esr_val = KASAN_ESR_VAL, | ||
1020 | .esr_mask = KASAN_ESR_MASK, | ||
1021 | .fn = kasan_handler, | ||
1022 | }; | ||
1023 | #endif | ||
1024 | |||
972 | /* | 1025 | /* |
973 | * Initial handler for AArch64 BRK exceptions | 1026 | * Initial handler for AArch64 BRK exceptions |
974 | * This handler only used until debug_traps_init(). | 1027 | * This handler only used until debug_traps_init(). |
@@ -976,6 +1029,10 @@ static struct break_hook bug_break_hook = { | |||
976 | int __init early_brk64(unsigned long addr, unsigned int esr, | 1029 | int __init early_brk64(unsigned long addr, unsigned int esr, |
977 | struct pt_regs *regs) | 1030 | struct pt_regs *regs) |
978 | { | 1031 | { |
1032 | #ifdef CONFIG_KASAN_SW_TAGS | ||
1033 | if ((esr & KASAN_ESR_MASK) == KASAN_ESR_VAL) | ||
1034 | return kasan_handler(regs, esr) != DBG_HOOK_HANDLED; | ||
1035 | #endif | ||
979 | return bug_handler(regs, esr) != DBG_HOOK_HANDLED; | 1036 | return bug_handler(regs, esr) != DBG_HOOK_HANDLED; |
980 | } | 1037 | } |
981 | 1038 | ||
@@ -983,4 +1040,7 @@ int __init early_brk64(unsigned long addr, unsigned int esr, | |||
983 | void __init trap_init(void) | 1040 | void __init trap_init(void) |
984 | { | 1041 | { |
985 | register_break_hook(&bug_break_hook); | 1042 | register_break_hook(&bug_break_hook); |
1043 | #ifdef CONFIG_KASAN_SW_TAGS | ||
1044 | register_break_hook(&kasan_break_hook); | ||
1045 | #endif | ||
986 | } | 1046 | } |
diff --git a/include/linux/kasan.h b/include/linux/kasan.h index a477ce2abdc9..8da7b7a4397a 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h | |||
@@ -173,6 +173,9 @@ void kasan_init_tags(void); | |||
173 | 173 | ||
174 | void *kasan_reset_tag(const void *addr); | 174 | void *kasan_reset_tag(const void *addr); |
175 | 175 | ||
176 | void kasan_report(unsigned long addr, size_t size, | ||
177 | bool is_write, unsigned long ip); | ||
178 | |||
176 | #else /* CONFIG_KASAN_SW_TAGS */ | 179 | #else /* CONFIG_KASAN_SW_TAGS */ |
177 | 180 | ||
178 | static inline void kasan_init_tags(void) { } | 181 | static inline void kasan_init_tags(void) { } |