Enable UBSAN traps for x86, which provides better reporting through
metadata encodeded into UD1. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmbpM6ITHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoU/kEACWS7Z9mQrWB3r22ufTTPoN+hNudth+ CP8wluXZGvLPh1Pq9dpB9ZniBUN8levYoGyj3NTdr6VtoMJ6NYcZVuH98lCCEMXO 1UmDpydSGZ3BqVgmf4h0eYAJgEiA5qTflXMsh6SfsaPQR7jniJTE451hgJdRIogG DvgWeVTYn5vt0+oRHJp6ogRLR9oOUgdp94fIwaW34OpesbVJeWUW9zAvBcqdNrDT KJIM7ta6eivEakFRxriQZTKRc+3ElvZ2fdWNdo9qrRd64MTIOTXAj3G0lXt3YtpZ 06pfJ1CfQ+nwHKfxmmy4gz4eJG7KcpMM+KFZTR3NoSAz4oMTzAvVTxAuEt+pahx6 bmLzaY/I/gRB/Rt+e5oEZSEIq+Sh/Lm3IZoQUhK0+HeJBjwPghBZw3BjkFJvEsMw S0arvklH2x37gP9rnzOODf2QG7aIAqLTrvRJS610fctwadR4k+2UIE8ZGHOTt55J UdiK/QhU4gMVaRTebTcPquu3IMmnJjla/bEWdIrBtOSiGtVd1BnAp/kvmkdQH3eI ZUqJbnfofN4rzSufFqSVY88ORVIcQMnNDLM0qyJofIC79u7OiU40icoDxWS6mDHQ wQSEszInhwNzyAxoHnNkXDunjDVKhATQPOde0F4TxLcrYD9KRpvJag/1j5fCQi+0 ftODZflfGS2UjQ== =Z5Hg -----END PGP SIGNATURE----- Merge tag 'x86-core-2024-09-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x86 core update from Thomas Gleixner: "Enable UBSAN traps for x86, which provides better reporting through metadata encodeded into UD1" * tag 'x86-core-2024-09-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/traps: Enable UBSAN traps on x86
This commit is contained in:
commit
dea435d397
@ -13,6 +13,18 @@
|
||||
#define INSN_UD2 0x0b0f
|
||||
#define LEN_UD2 2
|
||||
|
||||
/*
|
||||
* In clang we have UD1s reporting UBSAN failures on X86, 64 and 32bit.
|
||||
*/
|
||||
#define INSN_ASOP 0x67
|
||||
#define OPCODE_ESCAPE 0x0f
|
||||
#define SECOND_BYTE_OPCODE_UD1 0xb9
|
||||
#define SECOND_BYTE_OPCODE_UD2 0x0b
|
||||
|
||||
#define BUG_NONE 0xffff
|
||||
#define BUG_UD1 0xfffe
|
||||
#define BUG_UD2 0xfffd
|
||||
|
||||
#ifdef CONFIG_GENERIC_BUG
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/ubsan.h>
|
||||
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/processor.h>
|
||||
@ -91,6 +92,47 @@ __always_inline int is_valid_bugaddr(unsigned long addr)
|
||||
return *(unsigned short *)addr == INSN_UD2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for UD1 or UD2, accounting for Address Size Override Prefixes.
|
||||
* If it's a UD1, get the ModRM byte to pass along to UBSan.
|
||||
*/
|
||||
__always_inline int decode_bug(unsigned long addr, u32 *imm)
|
||||
{
|
||||
u8 v;
|
||||
|
||||
if (addr < TASK_SIZE_MAX)
|
||||
return BUG_NONE;
|
||||
|
||||
v = *(u8 *)(addr++);
|
||||
if (v == INSN_ASOP)
|
||||
v = *(u8 *)(addr++);
|
||||
if (v != OPCODE_ESCAPE)
|
||||
return BUG_NONE;
|
||||
|
||||
v = *(u8 *)(addr++);
|
||||
if (v == SECOND_BYTE_OPCODE_UD2)
|
||||
return BUG_UD2;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_UBSAN_TRAP) || v != SECOND_BYTE_OPCODE_UD1)
|
||||
return BUG_NONE;
|
||||
|
||||
/* Retrieve the immediate (type value) for the UBSAN UD1 */
|
||||
v = *(u8 *)(addr++);
|
||||
if (X86_MODRM_RM(v) == 4)
|
||||
addr++;
|
||||
|
||||
*imm = 0;
|
||||
if (X86_MODRM_MOD(v) == 1)
|
||||
*imm = *(u8 *)addr;
|
||||
else if (X86_MODRM_MOD(v) == 2)
|
||||
*imm = *(u32 *)addr;
|
||||
else
|
||||
WARN_ONCE(1, "Unexpected MODRM_MOD: %u\n", X86_MODRM_MOD(v));
|
||||
|
||||
return BUG_UD1;
|
||||
}
|
||||
|
||||
|
||||
static nokprobe_inline int
|
||||
do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
|
||||
struct pt_regs *regs, long error_code)
|
||||
@ -216,6 +258,8 @@ static inline void handle_invalid_op(struct pt_regs *regs)
|
||||
static noinstr bool handle_bug(struct pt_regs *regs)
|
||||
{
|
||||
bool handled = false;
|
||||
int ud_type;
|
||||
u32 imm;
|
||||
|
||||
/*
|
||||
* Normally @regs are unpoisoned by irqentry_enter(), but handle_bug()
|
||||
@ -223,7 +267,8 @@ static noinstr bool handle_bug(struct pt_regs *regs)
|
||||
* irqentry_enter().
|
||||
*/
|
||||
kmsan_unpoison_entry_regs(regs);
|
||||
if (!is_valid_bugaddr(regs->ip))
|
||||
ud_type = decode_bug(regs->ip, &imm);
|
||||
if (ud_type == BUG_NONE)
|
||||
return handled;
|
||||
|
||||
/*
|
||||
@ -236,10 +281,14 @@ static noinstr bool handle_bug(struct pt_regs *regs)
|
||||
*/
|
||||
if (regs->flags & X86_EFLAGS_IF)
|
||||
raw_local_irq_enable();
|
||||
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN ||
|
||||
handle_cfi_failure(regs) == BUG_TRAP_TYPE_WARN) {
|
||||
regs->ip += LEN_UD2;
|
||||
handled = true;
|
||||
if (ud_type == BUG_UD2) {
|
||||
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN ||
|
||||
handle_cfi_failure(regs) == BUG_TRAP_TYPE_WARN) {
|
||||
regs->ip += LEN_UD2;
|
||||
handled = true;
|
||||
}
|
||||
} else if (IS_ENABLED(CONFIG_UBSAN_TRAP)) {
|
||||
pr_crit("%s at %pS\n", report_ubsan_failure(regs, imm), (void *)regs->ip);
|
||||
}
|
||||
if (regs->flags & X86_EFLAGS_IF)
|
||||
raw_local_irq_disable();
|
||||
|
@ -4,6 +4,11 @@
|
||||
|
||||
#ifdef CONFIG_UBSAN_TRAP
|
||||
const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type);
|
||||
#else
|
||||
static inline const char *report_ubsan_failure(struct pt_regs *regs, u32 check_type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -29,8 +29,8 @@ config UBSAN_TRAP
|
||||
|
||||
Also note that selecting Y will cause your kernel to Oops
|
||||
with an "illegal instruction" error with no further details
|
||||
when a UBSAN violation occurs. (Except on arm64, which will
|
||||
report which Sanitizer failed.) This may make it hard to
|
||||
when a UBSAN violation occurs. (Except on arm64 and x86, which
|
||||
will report which Sanitizer failed.) This may make it hard to
|
||||
determine whether an Oops was caused by UBSAN or to figure
|
||||
out the details of a UBSAN violation. It makes the kernel log
|
||||
output less useful for bug reports.
|
||||
|
Loading…
Reference in New Issue
Block a user