From: Kees Cook <kees@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>
Cc: "Kees Cook" <kees@kernel.org>,
"Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>,
"Zhen Lei" <thunder.leizhen@huawei.com>,
"Arnd Bergmann" <arnd@arndb.de>,
"Michał Pecio" <michal.pecio@gmail.com>,
"Sebastian Andrzej Siewior" <bigeasy@linutronix.de>,
"Sami Tolvanen" <samitolvanen@google.com>,
linux-arm-kernel@lists.infradead.org,
"Nathan Chancellor" <nathan@kernel.org>,
"Vegard Nossum" <vegard.nossum@oracle.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Linus Walleij" <linus.walleij@linaro.org>,
"Jeff Johnson" <jeff.johnson@oss.qualcomm.com>,
"Randy Dunlap" <rdunlap@infradead.org>,
"David Woodhouse" <dwmw2@infradead.org>,
"Nick Desaulniers" <nick.desaulniers+lkml@gmail.com>,
"Bill Wendling" <morbo@google.com>,
"Justin Stitt" <justinstitt@google.com>,
"Marco Elver" <elver@google.com>,
"Przemek Kitszel" <przemyslaw.kitszel@intel.com>,
"Ramon de C Valle" <rcvalle@google.com>,
"Jonathan Corbet" <corbet@lwn.net>,
"Paul E. McKenney" <paulmck@kernel.org>,
"Nicolas Schier" <nicolas.schier@linux.dev>,
"Masahiro Yamada" <masahiroy@kernel.org>,
"Krzysztof Kozlowski" <krzysztof.kozlowski@linaro.org>,
"Mark Rutland" <mark.rutland@arm.com>,
linux-kernel@vger.kernel.org, llvm@lists.linux.dev,
linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org,
x86@kernel.org, linux-hardening@vger.kernel.org
Subject: [PATCH v2 9/9] ARM: traps: Implement KCFI trap handler for ARM32
Date: Wed, 3 Sep 2025 20:46:48 -0700 [thread overview]
Message-ID: <20250904034656.3670313-9-kees@kernel.org> (raw)
In-Reply-To: <20250904033217.it.414-kees@kernel.org>
ARM32 KCFI violations currently show as generic "Oops - undefined
instruction" errors, making debugging CFI failures difficult. Add a
proper KCFI trap handler similar to the aarch64 implementation to
provide clear CFI error messages.
The GCC ARM32 KCFI implementation uses UDF instructions with a specific
encoding pattern:
- UDF instruction format: cccc 0111 1111 imm12 1111 imm4
- 16-bit immediate reconstructed from bits 19-8 and 3-0
- KCFI encoding: 0x8000 | (type_reg_num << 5) | (target_reg_num & 31)
- Bit 15: KCFI trap identifier (0x8000)
- Bits 9-5: Type ID register field (0x1F when unavailable)
- Bits 4-0: Target address register number
When the type register field is 0x1F (unavailable due to stack
spilling), the handler walks back up to 5 preceding instructions to
locate the movw/movt instruction pair that loads the 32-bit type ID,
similar to x86 CFI trap reconstruction.
The undef_hook pattern matching includes the KCFI bit requirement to
ensure the handler is only called for KCFI violations, not arbitrary
UDF instructions.
Signed-off-by: Kees Cook <kees@kernel.org>
---
Cc: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Cc: Zhen Lei <thunder.leizhen@huawei.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: "Michał Pecio" <michal.pecio@gmail.com>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Sami Tolvanen <samitolvanen@google.com>
Cc: <linux-arm-kernel@lists.infradead.org>
---
arch/arm/kernel/traps.c | 102 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index afbd2ebe5c39..f2e4e18541e0 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -27,6 +27,7 @@
#include <linux/sched/task_stack.h>
#include <linux/irq.h>
#include <linux/vmalloc.h>
+#include <linux/cfi.h>
#include <linux/atomic.h>
#include <asm/cacheflush.h>
@@ -40,6 +41,7 @@
#include <asm/stacktrace.h>
#include <asm/system_misc.h>
#include <asm/opcodes.h>
+#include <linux/bitfield.h>
static const char *handler[]= {
@@ -685,6 +687,106 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
return 0;
}
+#ifdef CONFIG_CFI
+/*
+ * ARM32 KCFI trap handler.
+ * UDF instruction format: cccc 0111 1111 imm12 1111 imm4
+ * Immediate is reconstructed from bits 19-8 (12 bits) and bits 3-0 (4 bits)
+ * KCFI immediate encoding: 0x8000 | (0x1F << 5) | (target_reg_num & 31)
+ * - Bit 15: KCFI trap identifier (0x8000)
+ * - Bits 9-5: Type ID register field (0x1F when invalid due to stack spilling)
+ * - Bits 4-0: Target address register number
+ */
+#define CFI_UDF_KCFI_BIT BIT(15) /* KCFI identifier bit (0x8000) */
+#define CFI_UDF_IMM_TARGET GENMASK(4, 0) /* Target register (bits 4:0) */
+#define CFI_UDF_IMM_TYPE GENMASK(9, 5) /* Type register (bits 9:5) */
+
+/* UDF base pattern with KCFI bit: cond=0xe, 0x7f, xxxx, 1xxx, 0xf, xxxx */
+#define CFI_UDF_IMM_BASE 0xe7f008f0
+#define CFI_UDF_IMM_MASK 0xfff008f0 /* Mask for UDF + KCFI bit matching */
+
+static int cfi_udf_handler(struct pt_regs *regs, unsigned int instr)
+{
+ unsigned long target;
+ u32 target_reg, type_reg, type, imm16;
+
+ /* Reconstruct 16-bit immediate from bits 19-8 and 3-0 */
+ imm16 = ((instr >> 4) & 0xfff0) | (instr & 0x0f);
+
+ target_reg = FIELD_GET(CFI_UDF_IMM_TARGET, imm16);
+ type_reg = FIELD_GET(CFI_UDF_IMM_TYPE, imm16);
+
+ if (target_reg >= 16) {
+ pr_err("CFI UDF handler: invalid target register %u\n", target_reg);
+ return 1;
+ }
+
+ target = regs->uregs[target_reg];
+
+ /* Type register field is set to all 1s (0x1F) when invalid due to stack spilling */
+ if (type_reg == 0x1F) {
+ u32 *pc = (u32 *)regs->ARM_pc;
+ int i;
+
+ type = 0;
+ /* Walk back up to 5 instructions to find movw/movt pair for type ID */
+ for (i = 1; i <= 5; i++) {
+ u32 instr_prev = __mem_to_opcode_arm(pc[-i]);
+
+ /* Check for movw: cccc 0011 0000 imm4 Rd imm12 */
+ if ((instr_prev & 0x0ff00000) == 0x03000000) {
+ u32 imm16 = ((instr_prev >> 4) & 0xf000) | (instr_prev & 0xfff);
+ type |= imm16;
+ }
+ /* Check for movt: cccc 0011 0100 imm4 Rd imm12 */
+ else if ((instr_prev & 0x0ff00000) == 0x03400000) {
+ u32 imm16 = ((instr_prev >> 4) & 0xf000) | (instr_prev & 0xfff);
+ type |= (imm16 << 16);
+ }
+ }
+ if (type == 0)
+ pr_err("CFI UDF handler: failed to find type value\n");
+ } else {
+ if (type_reg >= 16) {
+ pr_err("CFI UDF handler: invalid type register %u\n", type_reg);
+ return 1;
+ }
+
+ type = regs->uregs[type_reg];
+ }
+
+ switch (report_cfi_failure(regs, regs->ARM_pc, &target, type)) {
+ case BUG_TRAP_TYPE_BUG:
+ die("Oops - CFI", regs, 0);
+ break;
+ case BUG_TRAP_TYPE_WARN:
+ break;
+ default:
+ return 1;
+ }
+
+ /* Skip the UDF instruction */
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+static struct undef_hook cfi_undef_hook = {
+ .instr_mask = CFI_UDF_IMM_MASK,
+ .instr_val = CFI_UDF_IMM_BASE,
+ .cpsr_mask = 0,
+ .cpsr_val = 0,
+ .fn = cfi_udf_handler,
+};
+
+static int __init arm_cfi_init(void)
+{
+ register_undef_hook(&cfi_undef_hook);
+ return 0;
+}
+
+early_initcall(arm_cfi_init);
+#endif /* CONFIG_CFI */
+
#ifdef CONFIG_TLS_REG_EMUL
/*
--
2.34.1
next prev parent reply other threads:[~2025-09-04 5:11 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-04 3:46 [PATCH v2 0/9] kcfi: Prepare for GCC support Kees Cook
2025-09-04 3:46 ` [PATCH v2 1/9] compiler_types.h: Move __nocfi out of compiler-specific header Kees Cook
2025-09-04 18:28 ` Nathan Chancellor
2025-09-04 3:46 ` [PATCH v2 2/9] x86/traps: Clarify KCFI instruction layout Kees Cook
2025-09-04 3:46 ` [PATCH v2 3/9] x86/cfi: Document the "cfi=" bootparam options Kees Cook
2025-09-04 18:32 ` Nathan Chancellor
2025-09-04 3:46 ` [PATCH v2 4/9] x86/cfi: Standardize on common "CFI:" prefix for CFI reports Kees Cook
2025-09-04 18:40 ` Nathan Chancellor
2025-09-05 0:40 ` Kees Cook
2025-09-04 3:46 ` [PATCH v2 5/9] x86/cfi: Add "debug" option to "cfi=" bootparam Kees Cook
2025-09-04 7:00 ` Peter Zijlstra
2025-09-04 3:46 ` [PATCH v2 6/9] x86/cfi: Remove __noinitretpoline and __noretpoline Kees Cook
2025-09-04 3:46 ` [PATCH v2 7/9] kconfig: Add transitional symbol attribute for migration support Kees Cook
2025-09-04 3:46 ` [PATCH v2 8/9] kcfi: Rename CONFIG_CFI_CLANG to CONFIG_CFI Kees Cook
2025-09-04 3:46 ` Kees Cook [this message]
2025-09-04 7:04 ` [PATCH v2 0/9] kcfi: Prepare for GCC support Peter Zijlstra
2025-09-04 16:37 ` Kees Cook
2025-09-04 20:01 ` Peter Zijlstra
2025-09-05 0:42 ` Kees Cook
2025-09-05 7:44 ` Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250904034656.3670313-9-kees@kernel.org \
--to=kees@kernel.org \
--cc=arnd@arndb.de \
--cc=bigeasy@linutronix.de \
--cc=corbet@lwn.net \
--cc=dwmw2@infradead.org \
--cc=elver@google.com \
--cc=jeff.johnson@oss.qualcomm.com \
--cc=justinstitt@google.com \
--cc=krzysztof.kozlowski@linaro.org \
--cc=linus.walleij@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=llvm@lists.linux.dev \
--cc=mark.rutland@arm.com \
--cc=masahiroy@kernel.org \
--cc=michal.pecio@gmail.com \
--cc=morbo@google.com \
--cc=nathan@kernel.org \
--cc=nick.desaulniers+lkml@gmail.com \
--cc=nicolas.schier@linux.dev \
--cc=ojeda@kernel.org \
--cc=paulmck@kernel.org \
--cc=peterz@infradead.org \
--cc=przemyslaw.kitszel@intel.com \
--cc=rcvalle@google.com \
--cc=rdunlap@infradead.org \
--cc=rmk+kernel@armlinux.org.uk \
--cc=samitolvanen@google.com \
--cc=thunder.leizhen@huawei.com \
--cc=vegard.nossum@oracle.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.