mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-17 03:50:37 +00:00
A while ago [0] FineIBT started using the 0xEA instruction to raise #UD.
All existing parts will generate #UD in 64bit mode on that instruction.
However; Intel/AMD have not blessed using this instruction, it is on
their 'reserved' opcode list for future use.
Peter Anvin worked the committees and got use of 0xD6 blessed, it
shall be called UDB (per the next SDM or so), and it being a single
byte instruction is easy to slip into a single byte immediate -- as
is done by this very patch.
Reworking the FineIBT code to use UDB wasn't entirely trivial. Notably
the FineIBT-BHI1 case ran out of bytes. In order to condense the
encoding some it was required to move the hash register from R10D to
EAX (thanks hpa!).
Per the x86_64 ABI, RAX is used to pass the number of vector registers
for vararg function calls -- something that should not happen in the
kernel. More so, the kernel is built with -mskip-rax-setup, which
should leave RAX completely unused, allowing its re-use.
[ For BPF; while the bpf2bpf tail-call uses RAX in its calling
convention, that does not use CFI and is unaffected. Only the
'regular' C->BPF transition is covered by CFI. ]
The ENDBR poison value is changed from 'OSP NOP3' to 'NOPL -42(%RAX)',
this is basically NOP4 but with UDB as its immediate. As such it is
still a non-standard NOP value unique to prior ENDBR sites, but now
also provides UDB.
Per Agner Fog's optimization guide, Jcc is assumed not-taken. That is,
the expected path should be the fallthrough case for improved
throughput.
Since the preamble now relies on the ENDBR poison to provide UDB, the
code is changed to write the poison right along with the initial
preamble -- this is possible because the ITS mitigation already
disabled IBT over rewriting the CFI scheme.
The scheme in detail:
Preamble:
FineIBT FineIBT-BHI1 FineIBT-BHI
__cfi_\func: __cfi_\func: __cfi_\func:
endbr endbr endbr
subl $0x12345678, %eax subl $0x12345678, %eax subl $0x12345678, %eax
jne.d32,np \func+3 cmovne %rax, %rdi cs cs call __bhi_args_N
jne.d8,np \func+3
\func: \func: \func:
nopl -42(%rax) nopl -42(%rax) nopl -42(%rax)
Notably there are 7 bytes available after the SUBL; this enables the
BHI1 case to fit without the nasty overlapping case it had previously.
The !BHI case uses Jcc.d32,np to consume all 7 bytes without the need
for an additional NOP, while the BHI case uses CS padding to align the
CALL with the end of the preamble such that it returns to \func+0.
Caller:
FineIBT Paranoid-FineIBT
fineibt_caller: fineibt_caller:
mov $0x12345678, %eax mov $0x12345678, %eax
lea -10(%r11), %r11 cmp -0x11(%r11), %eax
nop5 cs lea -0x10(%r11), %r11
retpoline: retpoline:
cs call __x86_indirect_thunk_r11 jne fineibt_caller+0xd
call *%r11
nop
Notably this is before apply_retpolines() which will fix up the
retpoline call -- since all parts with IBT also have eIBRS (lets
ignore ITS). Typically the retpoline site is rewritten (when still
intact) into:
call *%r11
nop3
[0] 06926c6cdb95 ("x86/ibt: Optimize the FineIBT instruction sequence")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20250901191307.GI4067720@noisy.programming.kicks-ass.net
111 lines
2.8 KiB
C
111 lines
2.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_X86_BUG_H
|
|
#define _ASM_X86_BUG_H
|
|
|
|
#include <linux/stringify.h>
|
|
#include <linux/instrumentation.h>
|
|
#include <linux/objtool.h>
|
|
#include <asm/asm.h>
|
|
|
|
/*
|
|
* Despite that some emulators terminate on UD2, we use it for WARN().
|
|
*/
|
|
#define ASM_UD2 _ASM_BYTES(0x0f, 0x0b)
|
|
#define INSN_UD2 0x0b0f
|
|
#define LEN_UD2 2
|
|
|
|
#define ASM_UDB _ASM_BYTES(0xd6)
|
|
#define INSN_UDB 0xd6
|
|
#define LEN_UDB 1
|
|
|
|
/*
|
|
* In clang we have UD1s reporting UBSAN failures on X86, 64 and 32bit.
|
|
*/
|
|
#define INSN_ASOP 0x67
|
|
#define INSN_LOCK 0xf0
|
|
#define OPCODE_ESCAPE 0x0f
|
|
#define SECOND_BYTE_OPCODE_UD1 0xb9
|
|
#define SECOND_BYTE_OPCODE_UD2 0x0b
|
|
|
|
#define BUG_NONE 0xffff
|
|
#define BUG_UD2 0xfffe
|
|
#define BUG_UD1 0xfffd
|
|
#define BUG_UD1_UBSAN 0xfffc
|
|
#define BUG_UDB 0xffd6
|
|
#define BUG_LOCK 0xfff0
|
|
|
|
#ifdef CONFIG_GENERIC_BUG
|
|
|
|
#ifdef CONFIG_X86_32
|
|
# define __BUG_REL(val) ".long " val
|
|
#else
|
|
# define __BUG_REL(val) ".long " val " - ."
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
|
#define __BUG_ENTRY(file, line, flags) \
|
|
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
|
|
"\t" __BUG_REL(file) "\t# bug_entry::file\n" \
|
|
"\t.word " line "\t# bug_entry::line\n" \
|
|
"\t.word " flags "\t# bug_entry::flags\n"
|
|
#else
|
|
#define __BUG_ENTRY(file, line, flags) \
|
|
"2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
|
|
"\t.word " flags "\t# bug_entry::flags\n"
|
|
#endif
|
|
|
|
#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \
|
|
"1:\t" ins "\n" \
|
|
".pushsection __bug_table,\"aw\"\n" \
|
|
__BUG_ENTRY(file, line, flags) \
|
|
"\t.org 2b + " size "\n" \
|
|
".popsection\n" \
|
|
extra
|
|
|
|
#define _BUG_FLAGS(ins, flags, extra) \
|
|
do { \
|
|
asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
|
|
"%c1", "%c2", "%c3", extra) \
|
|
: : "i" (__FILE__), "i" (__LINE__), \
|
|
"i" (flags), \
|
|
"i" (sizeof(struct bug_entry))); \
|
|
} while (0)
|
|
|
|
#define ARCH_WARN_ASM(file, line, flags, size) \
|
|
_BUG_FLAGS_ASM(ASM_UD2, file, line, flags, size, "")
|
|
|
|
#else
|
|
|
|
#define _BUG_FLAGS(ins, flags, extra) asm volatile(ins)
|
|
|
|
#endif /* CONFIG_GENERIC_BUG */
|
|
|
|
#define HAVE_ARCH_BUG
|
|
#define BUG() \
|
|
do { \
|
|
instrumentation_begin(); \
|
|
_BUG_FLAGS(ASM_UD2, 0, ""); \
|
|
__builtin_unreachable(); \
|
|
} while (0)
|
|
|
|
/*
|
|
* This instrumentation_begin() is strictly speaking incorrect; but it
|
|
* suppresses the complaints from WARN()s in noinstr code. If such a WARN()
|
|
* were to trigger, we'd rather wreck the machine in an attempt to get the
|
|
* message out than not know about it.
|
|
*/
|
|
|
|
#define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b)
|
|
|
|
#define __WARN_FLAGS(flags) \
|
|
do { \
|
|
__auto_type __flags = BUGFLAG_WARNING|(flags); \
|
|
instrumentation_begin(); \
|
|
_BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
|
|
instrumentation_end(); \
|
|
} while (0)
|
|
|
|
#include <asm-generic/bug.h>
|
|
|
|
#endif /* _ASM_X86_BUG_H */
|