[PATCH v3 11/14] x86: Handle int3 for inline KASAN reports

From: Maciej Wieczor-Retman
Date: Fri Apr 04 2025 - 09:19:24 EST


When a tag mismatch happens in inline software tag-based KASAN on x86 an
int3 instruction is executed and needs proper handling.

Call kasan_report() from the int3 handler and pass down the proper
information from registers - RDI should contain the problematic address
and RAX other metadata.

Also early return from the int3 selftest if inline KASAN is enabled
since it will cause a kernel panic otherwise.

Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx>
---
Changelog v3:
- Add this patch to the series.

arch/x86/kernel/alternative.c | 3 ++
arch/x86/kernel/traps.c | 52 +++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)

diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index bf82c6f7d690..ba277a25b57f 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -1979,6 +1979,9 @@ static noinline void __init int3_selftest(void)
};
unsigned int val = 0;

+ if (IS_ENABLED(CONFIG_KASAN_INLINE))
+ return;
+
BUG_ON(register_die_notifier(&int3_exception_nb));

/*
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 9f88b8a78e50..32c81fc2d439 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
#include <linux/kmsan.h>
+#include <linux/kasan.h>
#include <linux/spinlock.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
@@ -849,6 +850,51 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
cond_local_irq_disable(regs);
}

+#ifdef CONFIG_KASAN_SW_TAGS
+
+#define KASAN_RAX_RECOVER 0x20
+#define KASAN_RAX_WRITE 0x10
+#define KASAN_RAX_SIZE_MASK 0x0f
+#define KASAN_RAX_SIZE(rax) (1 << ((rax) & KASAN_RAX_SIZE_MASK))
+
+static bool kasan_handler(struct pt_regs *regs)
+{
+ int metadata = regs->ax;
+ u64 addr = regs->di;
+ u64 pc = regs->ip;
+ bool recover = metadata & KASAN_RAX_RECOVER;
+ bool write = metadata & KASAN_RAX_WRITE;
+ size_t size = KASAN_RAX_SIZE(metadata);
+
+ if (!IS_ENABLED(CONFIG_KASAN_INLINE))
+ return false;
+
+ if (user_mode(regs))
+ return false;
+
+ kasan_report((void *)addr, size, write, pc);
+
+ /*
+ * The instrumentation allows to control whether we can proceed after
+ * a crash was detected. This is done by passing the -recover flag to
+ * the compiler. Disabling recovery allows to generate more compact
+ * code.
+ *
+ * Unfortunately disabling recovery doesn't work for the kernel right
+ * now. KASAN reporting is disabled in some contexts (for example when
+ * the allocator accesses slab object metadata; this is controlled by
+ * current->kasan_depth). All these accesses are detected by the tool,
+ * even though the reports for them are not printed.
+ *
+ * This is something that might be fixed at some point in the future.
+ */
+ if (!recover)
+ die("Oops - KASAN", regs, 0);
+ return true;
+}
+
+#endif
+
static bool do_int3(struct pt_regs *regs)
{
int res;
@@ -863,6 +909,12 @@ static bool do_int3(struct pt_regs *regs)
if (kprobe_int3_handler(regs))
return true;
#endif
+
+#ifdef CONFIG_KASAN_SW_TAGS
+ if (kasan_handler(regs))
+ return true;
+#endif
+
res = notify_die(DIE_INT3, "int3", regs, 0, X86_TRAP_BP, SIGTRAP);

return res == NOTIFY_STOP;
--
2.49.0