From: Peter Zijlstra <peterz@infradead.org>
To: x86@kernel.org
Cc: linux-kernel@vger.kernel.org, peterz@infradead.org,
alyssa.milburn@intel.com, scott.d.constable@intel.com,
joao@overdrivepizza.com, andrew.cooper3@citrix.com,
jpoimboe@kernel.org, jose.marchesi@oracle.com,
hjl.tools@gmail.com, ndesaulniers@google.com,
samitolvanen@google.com, nathan@kernel.org, ojeda@kernel.org,
kees@kernel.org, alexei.starovoitov@gmail.com,
mhiramat@kernel.org, jmill@asu.edu
Subject: [PATCH v4 07/10] x86/ibt: Add paranoid FineIBT mode
Date: Mon, 24 Feb 2025 13:37:10 +0100 [thread overview]
Message-ID: <20250224124200.598033084@infradead.org> (raw)
In-Reply-To: 20250224123703.843199044@infradead.org
Due to concerns about circumvention attacks against FineIBT on 'naked'
ENDBR, add an additional caller side hash check to FineIBT. This
should make it impossible to pivot over such a 'naked' ENDBR
instruction at the cost of an additional load.
The specific pivot reported was against the SYSCALL entry site and
FRED will have all those holes fixed up.
https://lore.kernel.org/linux-hardening/Z60NwR4w%2F28Z7XUa@ubun/
This specific fineibt_paranoid_start[] sequence was concocted by
Scott.
Reported-by: Jennifer Miller <jmill@asu.edu>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/kernel/alternative.c | 144 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 138 insertions(+), 6 deletions(-)
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -741,6 +741,11 @@ void __init_or_module noinline apply_ret
op2 = insn.opcode.bytes[1];
switch (op1) {
+ case 0x70 ... 0x7f: /* Jcc.d8 */
+ /* See cfi_paranoid. */
+ WARN_ON_ONCE(cfi_mode != CFI_FINEIBT);
+ continue;
+
case CALL_INSN_OPCODE:
case JMP32_INSN_OPCODE:
break;
@@ -994,6 +999,8 @@ u32 cfi_get_func_hash(void *func)
static bool cfi_rand __ro_after_init = true;
static u32 cfi_seed __ro_after_init;
+static bool cfi_paranoid __ro_after_init = false;
+
/*
* Re-hash the CFI hash with a boot-time seed while making sure the result is
* not a valid ENDBR instruction.
@@ -1036,6 +1043,12 @@ static __init int cfi_parse_cmdline(char
} else if (!strcmp(str, "warn")) {
pr_alert("CFI mismatch non-fatal!\n");
cfi_warn = true;
+ } else if (!strcmp(str, "paranoid")) {
+ if (cfi_mode == CFI_FINEIBT) {
+ cfi_paranoid = true;
+ } else {
+ pr_err("Ignoring paranoid; depends on fineibt.\n");
+ }
} else {
pr_err("Ignoring unknown cfi option (%s).", str);
}
@@ -1124,6 +1137,52 @@ extern u8 fineibt_caller_end[];
#define fineibt_caller_jmp (fineibt_caller_size - 2)
+/*
+ * Since FineIBT does hash validation on the callee side it is prone to
+ * circumvention attacks where a 'naked' ENDBR instruction exists that
+ * is not part of the fineibt_preamble sequence.
+ *
+ * Notably the x86 entry points must be ENDBR and equally cannot be
+ * fineibt_preamble.
+ *
+ * The fineibt_paranoid caller sequence adds additional caller side
+ * hash validation. This stops such circumvention attacks dead, but at the cost
+ * of adding a load.
+ *
+ * <fineibt_paranoid_start>:
+ * 0: 41 ba 78 56 34 12 mov $0x12345678, %r10d
+ * 6: 45 3b 53 f7 cmp -0x9(%r11), %r10d
+ * a: 4d 8d 5b <f0> lea -0x10(%r11), %r11
+ * e: 75 fd jne d <fineibt_paranoid_start+0xd>
+ * 10: 41 ff d3 call *%r11
+ * 13: 90 nop
+ *
+ * Notably LEA does not modify flags and can be reordered with the CMP,
+ * avoiding a dependency. Again, using a non-taken (backwards) branch
+ * for the failure case, abusing LEA's immediate 0xf0 as LOCK prefix for the
+ * Jcc.d8, causing #UD.
+ */
+asm( ".pushsection .rodata \n"
+ "fineibt_paranoid_start: \n"
+ " movl $0x12345678, %r10d \n"
+ " cmpl -9(%r11), %r10d \n"
+ " lea -0x10(%r11), %r11 \n"
+ " jne fineibt_paranoid_start+0xd \n"
+ "fineibt_paranoid_ind: \n"
+ " call *%r11 \n"
+ " nop \n"
+ "fineibt_paranoid_end: \n"
+ ".popsection \n"
+);
+
+extern u8 fineibt_paranoid_start[];
+extern u8 fineibt_paranoid_ind[];
+extern u8 fineibt_paranoid_end[];
+
+#define fineibt_paranoid_size (fineibt_paranoid_end - fineibt_paranoid_start)
+#define fineibt_paranoid_ind (fineibt_paranoid_ind - fineibt_paranoid_start)
+#define fineibt_paranoid_ud 0xd
+
static u32 decode_preamble_hash(void *addr)
{
u8 *p = addr;
@@ -1287,18 +1346,48 @@ static int cfi_rewrite_callers(s32 *star
{
s32 *s;
+ BUG_ON(fineibt_paranoid_size != 20);
+
for (s = start; s < end; s++) {
void *addr = (void *)s + *s;
+ struct insn insn;
+ u8 bytes[20];
u32 hash;
+ int ret;
+ u8 op;
addr -= fineibt_caller_size;
hash = decode_caller_hash(addr);
- if (hash) {
+ if (!hash)
+ continue;
+
+ if (!cfi_paranoid) {
text_poke_early(addr, fineibt_caller_start, fineibt_caller_size);
WARN_ON(*(u32 *)(addr + fineibt_caller_hash) != 0x12345678);
text_poke_early(addr + fineibt_caller_hash, &hash, 4);
+ /* rely on apply_retpolines() */
+ continue;
+ }
+
+ /* cfi_paranoid */
+ ret = insn_decode_kernel(&insn, addr + fineibt_caller_size);
+ if (WARN_ON_ONCE(ret < 0))
+ continue;
+
+ op = insn.opcode.bytes[0];
+ if (op != CALL_INSN_OPCODE && op != JMP32_INSN_OPCODE) {
+ WARN_ON_ONCE(1);
+ continue;
}
- /* rely on apply_retpolines() */
+
+ memcpy(bytes, fineibt_paranoid_start, fineibt_paranoid_size);
+ memcpy(bytes + fineibt_caller_hash, &hash, 4);
+
+ ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind);
+ if (WARN_ON_ONCE(ret != 3))
+ continue;
+
+ text_poke_early(addr, bytes, fineibt_paranoid_size);
}
return 0;
@@ -1315,8 +1404,15 @@ static void __apply_fineibt(s32 *start_r
if (cfi_mode == CFI_AUTO) {
cfi_mode = CFI_KCFI;
- if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
+ if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT)) {
+ /*
+ * FRED has much saner context on exception entry and
+ * is less easy to take advantage of.
+ */
+ if (!cpu_feature_enabled(X86_FEATURE_FRED))
+ cfi_paranoid = true;
cfi_mode = CFI_FINEIBT;
+ }
}
/*
@@ -1373,8 +1469,10 @@ static void __apply_fineibt(s32 *start_r
/* now that nobody targets func()+0, remove ENDBR there */
cfi_rewrite_endbr(start_cfi, end_cfi);
- if (builtin)
- pr_info("Using FineIBT CFI\n");
+ if (builtin) {
+ pr_info("Using %sFineIBT CFI\n",
+ cfi_paranoid ? "paranoid " : "");
+ }
return;
default:
@@ -1447,7 +1545,7 @@ static void poison_cfi(void *addr)
* We check the preamble by checking for the ENDBR instruction relative to the
* 0xEA instruction.
*/
-bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type)
+static bool decode_fineibt_preamble(struct pt_regs *regs, unsigned long *target, u32 *type)
{
unsigned long addr = regs->ip - fineibt_preamble_ud;
u32 hash;
@@ -1472,6 +1570,40 @@ bool decode_fineibt_insn(struct pt_regs
return false;
}
+/*
+ * regs->ip points to a LOCK Jcc.d8 instruction from the fineibt_paranoid_start[]
+ * sequence.
+ */
+static bool decode_fineibt_paranoid(struct pt_regs *regs, unsigned long *target, u32 *type)
+{
+ unsigned long addr = regs->ip - fineibt_paranoid_ud;
+ u32 hash;
+
+ if (!cfi_paranoid || !is_cfi_trap(addr + fineibt_caller_size - LEN_UD2))
+ return false;
+
+ __get_kernel_nofault(&hash, addr + fineibt_caller_hash, u32, Efault);
+ *target = regs->r11 + fineibt_preamble_size;
+ *type = regs->r10;
+
+ /*
+ * Since the trapping instruction is the exact, but LOCK prefixed,
+ * Jcc.d8 that got us here, the normal fixup will work.
+ */
+ return true;
+
+Efault:
+ return false;
+}
+
+bool decode_fineibt_insn(struct pt_regs *regs, unsigned long *target, u32 *type)
+{
+ if (decode_fineibt_paranoid(regs, target, type))
+ return true;
+
+ return decode_fineibt_preamble(regs, target, type);
+}
+
#else
static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
next prev parent reply other threads:[~2025-02-24 12:46 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-24 12:37 [PATCH v4 00/10] x86/ibt: FineIBT-BHI Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 01/10] x86/cfi: Add warn option Peter Zijlstra
2025-02-24 18:57 ` Kees Cook
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/cfi: Add 'cfi=warn' boot option tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 02/10] x86/ibt: Add exact_endbr() helper Peter Zijlstra
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 03/10] x86/traps: Decode 0xEA #UD Peter Zijlstra
2025-02-24 18:58 ` Kees Cook
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/traps: Decode 0xEA instructions as #UD tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 04/10] x86/traps: Allow custom fixups in handle_bug() Peter Zijlstra
2025-02-24 18:59 ` Kees Cook
2025-02-25 8:54 ` Peter Zijlstra
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 05/10] x86/ibt: Optimize FineIBT sequence Peter Zijlstra
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/ibt: Optimize the FineIBT instruction sequence tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 06/10] x86/traps: Decode LOCK Jcc.d8 #UD Peter Zijlstra
2025-02-24 21:46 ` David Laight
2025-02-25 18:33 ` Kees Cook
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/traps: Decode LOCK Jcc.d8 as #UD tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` Peter Zijlstra [this message]
2025-02-24 19:00 ` [PATCH v4 07/10] x86/ibt: Add paranoid FineIBT mode Kees Cook
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 08/10] x86: BHI stubs Peter Zijlstra
2025-02-24 19:01 ` Kees Cook
2025-02-25 8:52 ` Peter Zijlstra
2025-02-25 18:31 ` Kees Cook
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/bhi: Add " tip-bot2 for Peter Zijlstra
2025-02-26 12:54 ` tip-bot2 for Peter Zijlstra
2025-02-24 12:37 ` [PATCH v4 09/10] x86/ibt: Implement FineIBT-BHI mitigation Peter Zijlstra
2025-02-25 9:12 ` Peter Zijlstra
2025-02-26 0:04 ` Constable, Scott D
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` tip-bot2 for Peter Zijlstra
2025-02-26 12:54 ` tip-bot2 for Peter Zijlstra
2025-02-26 19:53 ` Peter Zijlstra
2025-03-10 8:55 ` Peter Zijlstra
2025-03-10 16:00 ` Miguel Ojeda
2025-03-10 16:02 ` Peter Zijlstra
2025-03-11 19:09 ` Ramon de C Valle
2025-03-11 19:41 ` Miguel Ojeda
2025-03-11 20:23 ` Ramon de C Valle
2025-03-12 9:16 ` Peter Zijlstra
2025-03-12 11:36 ` Miguel Ojeda
2025-03-19 0:04 ` Nathan Chancellor
2025-02-24 12:37 ` [PATCH v4 10/10] x86/ibt: Optimize fineibt-bhi arity 1 case Peter Zijlstra
2025-02-26 10:54 ` [tip: x86/core] " tip-bot2 for Peter Zijlstra
2025-02-26 12:04 ` [tip: x86/core] x86/ibt: Optimize the " tip-bot2 for Peter Zijlstra
2025-02-26 12:54 ` tip-bot2 for Peter Zijlstra
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=20250224124200.598033084@infradead.org \
--to=peterz@infradead.org \
--cc=alexei.starovoitov@gmail.com \
--cc=alyssa.milburn@intel.com \
--cc=andrew.cooper3@citrix.com \
--cc=hjl.tools@gmail.com \
--cc=jmill@asu.edu \
--cc=joao@overdrivepizza.com \
--cc=jose.marchesi@oracle.com \
--cc=jpoimboe@kernel.org \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mhiramat@kernel.org \
--cc=nathan@kernel.org \
--cc=ndesaulniers@google.com \
--cc=ojeda@kernel.org \
--cc=samitolvanen@google.com \
--cc=scott.d.constable@intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox