arm64: Support Clang UBSAN trap codes for better reporting
When building with CONFIG_UBSAN_TRAP=y on arm64, Clang encodes the UBSAN check (handler) type in the esr. Extract this and actually report these traps as coming from the specific UBSAN check that tripped. Before: Internal error: BRK handler: 00000000f20003e8 [#1] PREEMPT SMP After: Internal error: UBSAN: shift out of bounds: 00000000f2005514 [#1] PREEMPT SMP Acked-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Mukesh Ojha <quic_mojha@quicinc.com> Reviewed-by: Fangrui Song <maskray@google.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will@kernel.org> Cc: John Stultz <jstultz@google.com> Cc: Yongqin Liu <yongqin.liu@linaro.org> Cc: Sami Tolvanen <samitolvanen@google.com> Cc: Yury Norov <yury.norov@gmail.com> Cc: Andrey Konovalov <andreyknvl@gmail.com> Cc: Marco Elver <elver@google.com> Cc: linux-arm-kernel@lists.infradead.org Cc: llvm@lists.linux.dev Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
parent
48df133578
commit
25b84002af
|
@ -17,6 +17,7 @@
|
|||
* 0x401: for compile time BRK instruction
|
||||
* 0x800: kernel-mode BUG() and WARN() traps
|
||||
* 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff)
|
||||
* 0x55xx: Undefined Behavior Sanitizer traps ('U' << 8)
|
||||
* 0x8xxx: Control-Flow Integrity traps
|
||||
*/
|
||||
#define KPROBES_BRK_IMM 0x004
|
||||
|
@ -28,6 +29,8 @@
|
|||
#define BUG_BRK_IMM 0x800
|
||||
#define KASAN_BRK_IMM 0x900
|
||||
#define KASAN_BRK_MASK 0x0ff
|
||||
#define UBSAN_BRK_IMM 0x5500
|
||||
#define UBSAN_BRK_MASK 0x00ff
|
||||
|
||||
#define CFI_BRK_IMM_TARGET GENMASK(4, 0)
|
||||
#define CFI_BRK_IMM_TYPE GENMASK(9, 5)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/ubsan.h>
|
||||
#include <linux/cfi.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
@ -1074,6 +1075,19 @@ static struct break_hook kasan_break_hook = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
static int ubsan_handler(struct pt_regs *regs, unsigned long esr)
|
||||
{
|
||||
die(report_ubsan_failure(regs, esr & UBSAN_BRK_MASK), regs, esr);
|
||||
return DBG_HOOK_HANDLED;
|
||||
}
|
||||
|
||||
static struct break_hook ubsan_break_hook = {
|
||||
.fn = ubsan_handler,
|
||||
.imm = UBSAN_BRK_IMM,
|
||||
.mask = UBSAN_BRK_MASK,
|
||||
};
|
||||
#endif
|
||||
|
||||
#define esr_comment(esr) ((esr) & ESR_ELx_BRK64_ISS_COMMENT_MASK)
|
||||
|
||||
|
@ -1091,6 +1105,10 @@ int __init early_brk64(unsigned long addr, unsigned long esr,
|
|||
#ifdef CONFIG_KASAN_SW_TAGS
|
||||
if ((esr_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
|
||||
return kasan_handler(regs, esr) != DBG_HOOK_HANDLED;
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
if ((esr_comment(esr) & ~UBSAN_BRK_MASK) == UBSAN_BRK_IMM)
|
||||
return ubsan_handler(regs, esr) != DBG_HOOK_HANDLED;
|
||||
#endif
|
||||
return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
|
||||
}
|
||||
|
@ -1104,6 +1122,9 @@ void __init trap_init(void)
|
|||
register_kernel_break_hook(&fault_break_hook);
|
||||
#ifdef CONFIG_KASAN_SW_TAGS
|
||||
register_kernel_break_hook(&kasan_break_hook);
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
register_kernel_break_hook(&ubsan_break_hook);
|
||||
#endif
|
||||
debug_traps_init();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_UBSAN_H
|
||||
#define _LINUX_UBSAN_H
|
||||
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type);
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -340,9 +340,7 @@ quiet_cmd_build_OID_registry = GEN $@
|
|||
clean-files += oid_registry_data.c
|
||||
|
||||
obj-$(CONFIG_UCS2_STRING) += ucs2_string.o
|
||||
ifneq ($(CONFIG_UBSAN_TRAP),y)
|
||||
obj-$(CONFIG_UBSAN) += ubsan.o
|
||||
endif
|
||||
|
||||
UBSAN_SANITIZE_ubsan.o := n
|
||||
KASAN_SANITIZE_ubsan.o := n
|
||||
|
|
68
lib/ubsan.c
68
lib/ubsan.c
|
@ -14,10 +14,76 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ubsan.h>
|
||||
#include <kunit/test-bug.h>
|
||||
|
||||
#include "ubsan.h"
|
||||
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
/*
|
||||
* Only include matches for UBSAN checks that are actually compiled in.
|
||||
* The mappings of struct SanitizerKind (the -fsanitize=xxx args) to
|
||||
* enum SanitizerHandler (the traps) in Clang is in clang/lib/CodeGen/.
|
||||
*/
|
||||
const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
|
||||
{
|
||||
switch (check_type) {
|
||||
#ifdef CONFIG_UBSAN_BOUNDS
|
||||
/*
|
||||
* SanitizerKind::ArrayBounds and SanitizerKind::LocalBounds
|
||||
* emit SanitizerHandler::OutOfBounds.
|
||||
*/
|
||||
case ubsan_out_of_bounds:
|
||||
return "UBSAN: array index out of bounds";
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_SHIFT
|
||||
/*
|
||||
* SanitizerKind::ShiftBase and SanitizerKind::ShiftExponent
|
||||
* emit SanitizerHandler::ShiftOutOfBounds.
|
||||
*/
|
||||
case ubsan_shift_out_of_bounds:
|
||||
return "UBSAN: shift out of bounds";
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_DIV_ZERO
|
||||
/*
|
||||
* SanitizerKind::IntegerDivideByZero emits
|
||||
* SanitizerHandler::DivremOverflow.
|
||||
*/
|
||||
case ubsan_divrem_overflow:
|
||||
return "UBSAN: divide/remainder overflow";
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_UNREACHABLE
|
||||
/*
|
||||
* SanitizerKind::Unreachable emits
|
||||
* SanitizerHandler::BuiltinUnreachable.
|
||||
*/
|
||||
case ubsan_builtin_unreachable:
|
||||
return "UBSAN: unreachable code";
|
||||
#endif
|
||||
#if defined(CONFIG_UBSAN_BOOL) || defined(CONFIG_UBSAN_ENUM)
|
||||
/*
|
||||
* SanitizerKind::Bool and SanitizerKind::Enum emit
|
||||
* SanitizerHandler::LoadInvalidValue.
|
||||
*/
|
||||
case ubsan_load_invalid_value:
|
||||
return "UBSAN: loading invalid value";
|
||||
#endif
|
||||
#ifdef CONFIG_UBSAN_ALIGNMENT
|
||||
/*
|
||||
* SanitizerKind::Alignment emits SanitizerHandler::TypeMismatch
|
||||
* or SanitizerHandler::AlignmentAssumption.
|
||||
*/
|
||||
case ubsan_alignment_assumption:
|
||||
return "UBSAN: alignment assumption";
|
||||
case ubsan_type_mismatch:
|
||||
return "UBSAN: type mismatch";
|
||||
#endif
|
||||
default:
|
||||
return "UBSAN: unrecognized failure code";
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
static const char * const type_check_kinds[] = {
|
||||
"load of",
|
||||
"store to",
|
||||
|
@ -384,3 +450,5 @@ void __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr,
|
|||
ubsan_epilogue();
|
||||
}
|
||||
EXPORT_SYMBOL(__ubsan_handle_alignment_assumption);
|
||||
|
||||
#endif /* !CONFIG_UBSAN_TRAP */
|
||||
|
|
32
lib/ubsan.h
32
lib/ubsan.h
|
@ -2,6 +2,38 @@
|
|||
#ifndef _LIB_UBSAN_H
|
||||
#define _LIB_UBSAN_H
|
||||
|
||||
/*
|
||||
* ABI defined by Clang's UBSAN enum SanitizerHandler:
|
||||
* https://github.com/llvm/llvm-project/blob/release/16.x/clang/lib/CodeGen/CodeGenFunction.h#L113
|
||||
*/
|
||||
enum ubsan_checks {
|
||||
ubsan_add_overflow,
|
||||
ubsan_builtin_unreachable,
|
||||
ubsan_cfi_check_fail,
|
||||
ubsan_divrem_overflow,
|
||||
ubsan_dynamic_type_cache_miss,
|
||||
ubsan_float_cast_overflow,
|
||||
ubsan_function_type_mismatch,
|
||||
ubsan_implicit_conversion,
|
||||
ubsan_invalid_builtin,
|
||||
ubsan_invalid_objc_cast,
|
||||
ubsan_load_invalid_value,
|
||||
ubsan_missing_return,
|
||||
ubsan_mul_overflow,
|
||||
ubsan_negate_overflow,
|
||||
ubsan_nullability_arg,
|
||||
ubsan_nullability_return,
|
||||
ubsan_nonnull_arg,
|
||||
ubsan_nonnull_return,
|
||||
ubsan_out_of_bounds,
|
||||
ubsan_pointer_overflow,
|
||||
ubsan_shift_out_of_bounds,
|
||||
ubsan_sub_overflow,
|
||||
ubsan_type_mismatch,
|
||||
ubsan_alignment_assumption,
|
||||
ubsan_vla_bound_not_positive,
|
||||
};
|
||||
|
||||
enum {
|
||||
type_kind_int = 0,
|
||||
type_kind_float = 1,
|
||||
|
|
Loading…
Reference in New Issue