Commit 25b84002 authored by Kees Cook's avatar Kees Cook
Browse files

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: default avatarMark Rutland <mark.rutland@arm.com>
Reviewed-by: default avatarArd Biesheuvel <ardb@kernel.org>
Acked-by: default avatarMukesh Ojha <quic_mojha@quicinc.com>
Reviewed-by: default avatarFangrui 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: default avatarKees Cook <keescook@chromium.org>
parent 48df1335
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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)
+21 −0
Original line number Diff line number Diff line
@@ -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();
}

include/linux/ubsan.h

0 → 100644
+9 −0
Original line number Diff line number Diff line
/* 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
+0 −2
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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 */
Loading