From nobody Sun Dec 14 17:58:40 2025 Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 061DA1B85F8 for ; Wed, 10 Dec 2025 17:30:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765387815; cv=none; b=Z72Qq/vdoA5FowLN49GvcKyrbDRDzC3OzSfIB6MlZ0UsowfE35YHSUEMH2wiDpa+T2BzPtSrOybjZ83pcmNYU8eRI3Ad5ZI0AZsuTzU8IJFNKgSqB4jyi73kyvQB9O5O2/rf7V0AlmsA6g6p7FwkNhorpXQc6gXrwMQ9dFTKN3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765387815; c=relaxed/simple; bh=LMaezUJWZi8w9+psRoT2CJOKvmM8RmxmTEKWOhg42fQ=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qF4G9I91fbyX4X6MJPvJItXp6c6OJWDlC1mCmEPf14f8bCjnZNd1jVoCRRgWzxLMBfpoBc+bdQnk8ti9vbjMbNiRGY1VzG22lvUSUXgdUjqFVIIxTiv25f3SzHAHqCuWN00TncwxR4qKWIBO5o4UDjxqpTjqynjLAcmRWJfwt94= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=bff9AUQ6; arc=none smtp.client-ip=185.70.43.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="bff9AUQ6" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1765387809; x=1765647009; bh=lY9fE4khm8TKePDz5X4aC5hOywz6v3YeXyq28VGtz8g=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=bff9AUQ6oevu1qmq4awuQxoIpaVMBvgWcsrkovnFdMLLnKhFnUAmFyidjfDgyvHe0 3M+9lDnYM56Ifcsfm8YJHY4kunEYYxx4B3eqknKTYzC90GMoxesWB/vM2z5YTTgnlG OwF9x6HqTwCREXSowqU+JpDm/q+L85JSm63BxpFVdj3APlRs2kyqFLbGiLQpZwrFBG bhdKnEsiWTo2Pdwtf0mz4JPCpeXzqJVBC0Z3hUXFC5fmTu1J620kB3ztHCNyFivZME Sc4eqgZlfsssXT7B0EHb+HkkhLusIRaALPc610UuPoh1fkiWsyzVtTZbEVLRh9UpmH NGkFThngFKhag== Date: Wed, 10 Dec 2025 17:30:05 +0000 To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Andrey Ryabinin , Alexander Potapenko , Andrey Konovalov , Dmitry Vyukov , Vincenzo Frascino , Andy Lutomirski , Peter Zijlstra , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt From: Maciej Wieczor-Retman Cc: m.wieczorretman@pm.me, Maciej Wieczor-Retman , linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com, llvm@lists.linux.dev Subject: [PATCH v7 12/15] x86/kasan: Handle UD1 for inline KASAN reports Message-ID: <13fa5da13adf927abbb7dd85d19fbaa8e4fadc84.1765386422.git.m.wieczorretman@pm.me> In-Reply-To: References: Feedback-ID: 164464600:user:proton X-Pm-Message-ID: 7032ee222089e9740a5e84130b4a9493439885f2 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Maciej Wieczor-Retman Inline KASAN on x86 should do tag mismatch reports by passing the metadata through the UD1 instruction and the faulty address through RDI, a scheme that's already used by UBSan and is easy to extend. The current LLVM way of passing KASAN software tag mode metadata is done using the INT3 instruction. However that should be changed because it doesn't align to how the kernel already handles UD1 for similar use cases. Since inline software tag-based KASAN doesn't work on x86 due to missing compiler support it can be fixed and the INT3 can be changed to UD1 at the same time. Add a kasan component to the #UD decoding and handling functions. Make part of that hook - which decides whether to die or recover from a tag mismatch - arch independent to avoid duplicating a long comment on both x86 and arm64 architectures. Signed-off-by: Maciej Wieczor-Retman --- Changelog v7: - Redo the #UD handling that's based on Peter Zijlstra WARN() patches. - Rename kasan_inline.c -> kasan_sw_tags.c (Alexander) Changelog v6: - Change the whole patch from using INT3 to UD1. Changelog v5: - Add die to argument list of kasan_inline_recover() in arch/arm64/kernel/traps.c. Changelog v4: - Make kasan_handler() a stub in a header file. Remove #ifdef from traps.c. - Consolidate the "recover" comment into one place. - Make small changes to the patch message. MAINTAINERS | 2 +- arch/x86/include/asm/bug.h | 1 + arch/x86/include/asm/kasan.h | 20 ++++++++++++++++++++ arch/x86/kernel/traps.c | 13 ++++++++++++- arch/x86/mm/Makefile | 2 ++ arch/x86/mm/kasan_sw_tags.c | 19 +++++++++++++++++++ include/linux/kasan.h | 23 +++++++++++++++++++++++ 7 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 arch/x86/mm/kasan_sw_tags.c diff --git a/MAINTAINERS b/MAINTAINERS index a591598cc4b5..ff1c036ae39f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13421,7 +13421,7 @@ S: Maintained B: https://bugzilla.kernel.org/buglist.cgi?component=3DSanitizers&product= =3DMemory%20Management F: Documentation/dev-tools/kasan.rst F: arch/*/include/asm/*kasan*.h -F: arch/*/mm/kasan_init* +F: arch/*/mm/kasan_* F: include/linux/kasan*.h F: lib/Kconfig.kasan F: mm/kasan/ diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h index 83b0fb38732d..eb733ac14598 100644 --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h @@ -32,6 +32,7 @@ #define BUG_UD1 0xfffd #define BUG_UD1_UBSAN 0xfffc #define BUG_UD1_WARN 0xfffb +#define BUG_UD1_KASAN 0xfffa #define BUG_UDB 0xffd6 #define BUG_LOCK 0xfff0 =20 diff --git a/arch/x86/include/asm/kasan.h b/arch/x86/include/asm/kasan.h index eab12527ed7f..6e083d45770d 100644 --- a/arch/x86/include/asm/kasan.h +++ b/arch/x86/include/asm/kasan.h @@ -6,6 +6,24 @@ #include #include #define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL) + +/* + * LLVM ABI for reporting tag mismatches in inline KASAN mode. + * On x86 the UD1 instruction is used to carry metadata in the ECX register + * to the KASAN report. ECX is used to differentiate KASAN from UBSan when + * decoding the UD1 instruction. + * + * SIZE refers to how many bytes the faulty memory access + * requested. + * WRITE bit, when set, indicates the access was a write, otherwise + * it was a read. + * RECOVER bit, when set, should allow the kernel to carry on after + * a tag mismatch. Otherwise die() is called. + */ +#define KASAN_ECX_RECOVER 0x20 +#define KASAN_ECX_WRITE 0x10 +#define KASAN_ECX_SIZE_MASK 0x0f +#define KASAN_ECX_SIZE(ecx) (1 << ((ecx) & KASAN_ECX_SIZE_MASK)) #define KASAN_SHADOW_SCALE_SHIFT 3 =20 /* @@ -34,10 +52,12 @@ #define __tag_shifted(tag) FIELD_PREP(GENMASK_ULL(60, 57), tag) #define __tag_reset(addr) (sign_extend64((u64)(addr), 56)) #define __tag_get(addr) ((u8)FIELD_GET(GENMASK_ULL(60, 57), (u64)addr)) +void kasan_inline_handler(struct pt_regs *regs, unsigned int metadata, u64= addr); #else #define __tag_shifted(tag) 0UL #define __tag_reset(addr) (addr) #define __tag_get(addr) 0 +static inline void kasan_inline_handler(struct pt_regs *regs, unsigned int= metadata, u64 addr) { } #endif /* CONFIG_KASAN_SW_TAGS */ =20 #ifdef CONFIG_64BIT diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index cb324cc1fd99..e55e5441fc83 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -102,6 +102,7 @@ __always_inline int is_valid_bugaddr(unsigned long addr) * FineIBT: f0 75 f9 lock jne . - 6 * UBSan{0}: 67 0f b9 00 ud1 (%eax),%eax * UBSan{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax + * KASAN: 48 0f b9 41 XX ud1 0xXX(%rcx),%reg * static_call: 0f b9 cc ud1 %esp,%ecx * __WARN_trap: 67 48 0f b9 3a ud1 (%edx),%reg * @@ -190,6 +191,10 @@ __always_inline int decode_bug(unsigned long addr, s32= *imm, int *len) addr +=3D 1; if (rm =3D=3D 0) /* (%eax) */ type =3D BUG_UD1_UBSAN; + if (rm =3D=3D 1) { /* (%ecx) */ + type =3D BUG_UD1_KASAN; + *imm +=3D reg << 8; + } break; =20 case 2: *imm =3D *(s32 *)addr; @@ -399,7 +404,7 @@ static inline void handle_invalid_op(struct pt_regs *re= gs) =20 static noinstr bool handle_bug(struct pt_regs *regs) { - unsigned long addr =3D regs->ip; + unsigned long kasan_addr, addr =3D regs->ip; bool handled =3D false; int ud_type, ud_len; s32 ud_imm; @@ -454,6 +459,12 @@ static noinstr bool handle_bug(struct pt_regs *regs) } break; =20 + case BUG_UD1_KASAN: + kasan_addr =3D (u64)pt_regs_val(regs, ud_imm >> 8); + kasan_inline_handler(regs, ud_imm, kasan_addr); + handled =3D true; + break; + default: break; } diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 5b9908f13dcf..b562963a866e 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -36,7 +36,9 @@ obj-$(CONFIG_PTDUMP) +=3D dump_pagetables.o obj-$(CONFIG_PTDUMP_DEBUGFS) +=3D debug_pagetables.o =20 KASAN_SANITIZE_kasan_init_$(BITS).o :=3D n +KASAN_SANITIZE_kasan_sw_tags.o :=3D n obj-$(CONFIG_KASAN) +=3D kasan_init_$(BITS).o +obj-$(CONFIG_KASAN_SW_TAGS) +=3D kasan_sw_tags.o =20 KMSAN_SANITIZE_kmsan_shadow.o :=3D n obj-$(CONFIG_KMSAN) +=3D kmsan_shadow.o diff --git a/arch/x86/mm/kasan_sw_tags.c b/arch/x86/mm/kasan_sw_tags.c new file mode 100644 index 000000000000..93b63be584fd --- /dev/null +++ b/arch/x86/mm/kasan_sw_tags.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +void kasan_inline_handler(struct pt_regs *regs, unsigned int metadata, u64= addr) +{ + u64 pc =3D regs->ip; + bool recover =3D metadata & KASAN_ECX_RECOVER; + bool write =3D metadata & KASAN_ECX_WRITE; + size_t size =3D KASAN_ECX_SIZE(metadata); + + if (user_mode(regs)) + return; + + if (!kasan_report((void *)addr, size, write, pc)) + return; + + kasan_die_unless_recover(recover, "Oops - KASAN", regs, metadata, die); +} diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 5cb21b90a2ec..03e263fb9fa1 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -669,4 +669,27 @@ void kasan_non_canonical_hook(unsigned long addr); static inline void kasan_non_canonical_hook(unsigned long addr) { } #endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ =20 +#ifdef CONFIG_KASAN_SW_TAGS +/* + * 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. + */ +static inline void kasan_die_unless_recover(bool recover, char *msg, struc= t pt_regs *regs, + unsigned long err, void die_fn(const char *str, struct pt_regs *regs, lon= g err)) +{ + if (!recover) + die_fn(msg, regs, err); +} +#endif + #endif /* LINUX_KASAN_H */ --=20 2.52.0