[PATCH v3 01/10] x86/cfi: Add warn option

Peter Zijlstra posted 10 patches 10 months ago
There is a newer version of this series
[PATCH v3 01/10] x86/cfi: Add warn option
Posted by Peter Zijlstra 10 months ago
Rebuilding with CFI_PERMISSIVE toggled is such a pain, esp. since
clang is so slow.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/include/asm/cfi.h    |    1 +
 arch/x86/kernel/alternative.c |    4 ++++
 arch/x86/kernel/cfi.c         |   14 ++++++++++----
 3 files changed, 15 insertions(+), 4 deletions(-)

--- a/arch/x86/include/asm/cfi.h
+++ b/arch/x86/include/asm/cfi.h
@@ -100,6 +100,7 @@ enum cfi_mode {
 };
 
 extern enum cfi_mode cfi_mode;
+extern bool cfi_warn;
 
 struct pt_regs;
 
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -916,6 +916,7 @@ void __init_or_module apply_seal_endbr(s
 #endif
 
 enum cfi_mode cfi_mode __ro_after_init = __CFI_DEFAULT;
+bool cfi_warn __ro_after_init = false;
 
 #ifdef CONFIG_CFI_CLANG
 struct bpf_insn;
@@ -1022,6 +1023,9 @@ static __init int cfi_parse_cmdline(char
 			cfi_mode = CFI_FINEIBT;
 		} else if (!strcmp(str, "norand")) {
 			cfi_rand = false;
+		} else if (!strcmp(str, "warn")) {
+			pr_alert("CFI mismatch non-fatal!\n");
+			cfi_warn = true;
 		} else {
 			pr_err("Ignoring unknown cfi option (%s).", str);
 		}
--- a/arch/x86/kernel/cfi.c
+++ b/arch/x86/kernel/cfi.c
@@ -67,16 +67,17 @@ static bool decode_cfi_insn(struct pt_re
  */
 enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
 {
-	unsigned long target;
+	unsigned long target, addr = regs->ip;
+	enum bug_trap_type btt;
 	u32 type;
 
 	switch (cfi_mode) {
 	case CFI_KCFI:
-		if (!is_cfi_trap(regs->ip))
+		if (!is_cfi_trap(addr))
 			return BUG_TRAP_TYPE_NONE;
 
 		if (!decode_cfi_insn(regs, &target, &type))
-			return report_cfi_failure_noaddr(regs, regs->ip);
+			return report_cfi_failure_noaddr(regs, addr);
 
 		break;
 
@@ -90,7 +91,12 @@ enum bug_trap_type handle_cfi_failure(st
 		return BUG_TRAP_TYPE_NONE;
 	}
 
-	return report_cfi_failure(regs, regs->ip, &target, type);
+	btt = report_cfi_failure(regs, addr, &target, type);
+	if (btt == BUG_TRAP_TYPE_BUG && cfi_warn) {
+		__warn(NULL, 0, (void *)addr, 0, regs, NULL);
+		btt = BUG_TRAP_TYPE_WARN;
+	}
+	return btt;
 }
 
 /*
Re: [PATCH v3 01/10] x86/cfi: Add warn option
Posted by Kees Cook 10 months ago
On Wed, Feb 19, 2025 at 05:21:08PM +0100, Peter Zijlstra wrote:
> Rebuilding with CFI_PERMISSIVE toggled is such a pain, esp. since
> clang is so slow.

This seems too complex; report_cfi_failure() already has the fail/warn
logic test. I would have expected cfi_warn to take CONFIG_CFI_PERMISSIVE
as a default instead, like:

+bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE);

and then just replace report_cfi_failure()'s check of
CONFIG_CFI_PERMISSIVE with cfi_warn:

-        if (IS_ENABLED(CONFIG_CFI_PERMISSIVE)) {
+        if (cfi_warn) {

-Kees

(I do worry about data-only attacks going after page tables and flipping
pages to r/w and changing cfi_warn to 1, but that's probably on the same
order of difficulty as targeting the cfi handler function itself. Hmpf.)

-- 
Kees Cook
Re: [PATCH v3 01/10] x86/cfi: Add warn option
Posted by Peter Zijlstra 10 months ago
On Wed, Feb 19, 2025 at 09:50:54AM -0800, Kees Cook wrote:
> On Wed, Feb 19, 2025 at 05:21:08PM +0100, Peter Zijlstra wrote:
> > Rebuilding with CFI_PERMISSIVE toggled is such a pain, esp. since
> > clang is so slow.
> 
> This seems too complex; report_cfi_failure() already has the fail/warn
> logic test. I would have expected cfi_warn to take CONFIG_CFI_PERMISSIVE
> as a default instead, like:
> 
> +bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE);

In kernel/cfi.c, yes that works.

I somehow got stuck with having cfi_warn in arch/x86 and then not being
able to use it in generic code. Been too busy to debug all the fun fails
to realize I could simply stick the variable in generic code.