Skip to content

Commit 41eea9c

Browse files
xairytorvalds
authored andcommitted
kasan, arm64: add brk handler for inline instrumentation
Tag-based KASAN inline instrumentation mode (which embeds checks of shadow memory into the generated code, instead of inserting a callback) generates a brk instruction when a tag mismatch is detected. This commit adds a tag-based KASAN specific brk handler, that decodes the immediate value passed to the brk instructions (to extract information about the memory access that triggered the mismatch), reads the register values (x0 contains the guilty address) and reports the bug. Link: http://lkml.kernel.org/r/c91fe7684070e34dc34b419e6b69498f4dcacc2d.1544099024.git.andreyknvl@google.com Signed-off-by: Andrey Konovalov <[email protected]> Reviewed-by: Andrey Ryabinin <[email protected]> Reviewed-by: Dmitry Vyukov <[email protected]> Acked-by: Will Deacon <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Mark Rutland <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7f94ffb commit 41eea9c

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

arch/arm64/include/asm/brk-imm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
* 0x400: for dynamic BRK instruction
1717
* 0x401: for compile time BRK instruction
1818
* 0x800: kernel-mode BUG() and WARN() traps
19+
* 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff)
1920
*/
2021
#define FAULT_BRK_IMM 0x100
2122
#define KGDB_DYN_DBG_BRK_IMM 0x400
2223
#define KGDB_COMPILED_DBG_BRK_IMM 0x401
2324
#define BUG_BRK_IMM 0x800
25+
#define KASAN_BRK_IMM 0x900
2426

2527
#endif

arch/arm64/kernel/traps.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <linux/sizes.h>
3636
#include <linux/syscalls.h>
3737
#include <linux/mm_types.h>
38+
#include <linux/kasan.h>
3839

3940
#include <asm/atomic.h>
4041
#include <asm/bug.h>
@@ -969,18 +970,77 @@ static struct break_hook bug_break_hook = {
969970
.fn = bug_handler,
970971
};
971972

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+
9721025
/*
9731026
* Initial handler for AArch64 BRK exceptions
9741027
* This handler only used until debug_traps_init().
9751028
*/
9761029
int __init early_brk64(unsigned long addr, unsigned int esr,
9771030
struct pt_regs *regs)
9781031
{
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
9791036
return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
9801037
}
9811038

9821039
/* This registration must happen early, before debug_traps_init(). */
9831040
void __init trap_init(void)
9841041
{
9851042
register_break_hook(&bug_break_hook);
1043+
#ifdef CONFIG_KASAN_SW_TAGS
1044+
register_break_hook(&kasan_break_hook);
1045+
#endif
9861046
}

include/linux/kasan.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ void kasan_init_tags(void);
173173

174174
void *kasan_reset_tag(const void *addr);
175175

176+
void kasan_report(unsigned long addr, size_t size,
177+
bool is_write, unsigned long ip);
178+
176179
#else /* CONFIG_KASAN_SW_TAGS */
177180

178181
static inline void kasan_init_tags(void) { }

0 commit comments

Comments
 (0)