Skip to content

Commit 8754e67

Browse files
pa1guptahansendc
authored andcommitted
x86/its: Add support for ITS-safe indirect thunk
Due to ITS, indirect branches in the lower half of a cacheline may be vulnerable to branch target injection attack. Introduce ITS-safe thunks to patch indirect branches in the lower half of cacheline with the thunk. Also thunk any eBPF generated indirect branches in emit_indirect_jump(). Below category of indirect branches are not mitigated: - Indirect branches in the .init section are not mitigated because they are discarded after boot. - Indirect branches that are explicitly marked retpoline-safe. Note that retpoline also mitigates the indirect branches against ITS. This is because the retpoline sequence fills an RSB entry before RET, and it does not suffer from RSB-underflow part of the ITS. Signed-off-by: Pawan Gupta <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Josh Poimboeuf <[email protected]> Reviewed-by: Alexandre Chartre <[email protected]>
1 parent 159013a commit 8754e67

File tree

7 files changed

+96
-4
lines changed

7 files changed

+96
-4
lines changed

arch/x86/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,6 +2710,17 @@ config MITIGATION_SSB
27102710
of speculative execution in a similar way to the Meltdown and Spectre
27112711
security vulnerabilities.
27122712

2713+
config MITIGATION_ITS
2714+
bool "Enable Indirect Target Selection mitigation"
2715+
depends on CPU_SUP_INTEL && X86_64
2716+
depends on MITIGATION_RETPOLINE && MITIGATION_RETHUNK
2717+
default y
2718+
help
2719+
Enable Indirect Target Selection (ITS) mitigation. ITS is a bug in
2720+
BPU on some Intel CPUs that may allow Spectre V2 style attacks. If
2721+
disabled, mitigation cannot be enabled via cmdline.
2722+
See <file:Documentation/admin-guide/hw-vuln/indirect-target-selection.rst>
2723+
27132724
endif
27142725

27152726
config ARCH_HAS_ADD_PAGES

arch/x86/include/asm/cpufeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@
481481
#define X86_FEATURE_AMD_HETEROGENEOUS_CORES (21*32 + 6) /* Heterogeneous Core Topology */
482482
#define X86_FEATURE_AMD_WORKLOAD_CLASS (21*32 + 7) /* Workload Classification */
483483
#define X86_FEATURE_PREFER_YMM (21*32 + 8) /* Avoid ZMM registers due to downclocking */
484+
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32 + 9) /* Use thunk for indirect branches in lower half of cacheline */
484485

485486
/*
486487
* BUG word(s)

arch/x86/include/asm/nospec-branch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,14 @@
336336

337337
#else /* __ASSEMBLER__ */
338338

339+
#define ITS_THUNK_SIZE 64
340+
339341
typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE];
342+
typedef u8 its_thunk_t[ITS_THUNK_SIZE];
340343
extern retpoline_thunk_t __x86_indirect_thunk_array[];
341344
extern retpoline_thunk_t __x86_indirect_call_thunk_array[];
342345
extern retpoline_thunk_t __x86_indirect_jump_thunk_array[];
346+
extern its_thunk_t __x86_indirect_its_thunk_array[];
343347

344348
#ifdef CONFIG_MITIGATION_RETHUNK
345349
extern void __x86_return_thunk(void);

arch/x86/kernel/alternative.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,8 @@ static int emit_indirect(int op, int reg, u8 *bytes)
581581
return i;
582582
}
583583

584-
static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes)
584+
static int __emit_trampoline(void *addr, struct insn *insn, u8 *bytes,
585+
void *call_dest, void *jmp_dest)
585586
{
586587
u8 op = insn->opcode.bytes[0];
587588
int i = 0;
@@ -602,15 +603,15 @@ static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8
602603
switch (op) {
603604
case CALL_INSN_OPCODE:
604605
__text_gen_insn(bytes+i, op, addr+i,
605-
__x86_indirect_call_thunk_array[reg],
606+
call_dest,
606607
CALL_INSN_SIZE);
607608
i += CALL_INSN_SIZE;
608609
break;
609610

610611
case JMP32_INSN_OPCODE:
611612
clang_jcc:
612613
__text_gen_insn(bytes+i, op, addr+i,
613-
__x86_indirect_jump_thunk_array[reg],
614+
jmp_dest,
614615
JMP32_INSN_SIZE);
615616
i += JMP32_INSN_SIZE;
616617
break;
@@ -625,6 +626,35 @@ static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8
625626
return i;
626627
}
627628

629+
static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes)
630+
{
631+
return __emit_trampoline(addr, insn, bytes,
632+
__x86_indirect_call_thunk_array[reg],
633+
__x86_indirect_jump_thunk_array[reg]);
634+
}
635+
636+
#ifdef CONFIG_MITIGATION_ITS
637+
static int emit_its_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes)
638+
{
639+
return __emit_trampoline(addr, insn, bytes,
640+
__x86_indirect_its_thunk_array[reg],
641+
__x86_indirect_its_thunk_array[reg]);
642+
}
643+
644+
/* Check if an indirect branch is at ITS-unsafe address */
645+
static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg)
646+
{
647+
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
648+
return false;
649+
650+
/* Indirect branch opcode is 2 or 3 bytes depending on reg */
651+
addr += 1 + reg / 8;
652+
653+
/* Lower-half of the cacheline? */
654+
return !(addr & 0x20);
655+
}
656+
#endif
657+
628658
/*
629659
* Rewrite the compiler generated retpoline thunk calls.
630660
*
@@ -699,6 +729,15 @@ static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes)
699729
bytes[i++] = 0xe8; /* LFENCE */
700730
}
701731

732+
#ifdef CONFIG_MITIGATION_ITS
733+
/*
734+
* Check if the address of last byte of emitted-indirect is in
735+
* lower-half of the cacheline. Such branches need ITS mitigation.
736+
*/
737+
if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + i, reg))
738+
return emit_its_trampoline(addr, insn, reg, bytes);
739+
#endif
740+
702741
ret = emit_indirect(op, reg, bytes + i);
703742
if (ret < 0)
704743
return ret;

arch/x86/kernel/vmlinux.lds.S

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@ PROVIDE(__ref_stack_chk_guard = __stack_chk_guard);
497497
"SRSO function pair won't alias");
498498
#endif
499499

500+
#if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
501+
. = ASSERT(__x86_indirect_its_thunk_rax & 0x20, "__x86_indirect_thunk_rax not in second half of cacheline");
502+
. = ASSERT(((__x86_indirect_its_thunk_rcx - __x86_indirect_its_thunk_rax) % 64) == 0, "Indirect thunks are not cacheline apart");
503+
. = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array");
504+
#endif
505+
500506
#endif /* CONFIG_X86_64 */
501507

502508
/*

arch/x86/lib/retpoline.S

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,34 @@ SYM_FUNC_END(call_depth_return_thunk)
367367

368368
#endif /* CONFIG_MITIGATION_CALL_DEPTH_TRACKING */
369369

370+
#ifdef CONFIG_MITIGATION_ITS
371+
372+
.macro ITS_THUNK reg
373+
374+
SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL)
375+
UNWIND_HINT_UNDEFINED
376+
ANNOTATE_NOENDBR
377+
ANNOTATE_RETPOLINE_SAFE
378+
jmp *%\reg
379+
int3
380+
.align 32, 0xcc /* fill to the end of the line */
381+
.skip 32, 0xcc /* skip to the next upper half */
382+
.endm
383+
384+
/* ITS mitigation requires thunks be aligned to upper half of cacheline */
385+
.align 64, 0xcc
386+
.skip 32, 0xcc
387+
SYM_CODE_START(__x86_indirect_its_thunk_array)
388+
389+
#define GEN(reg) ITS_THUNK reg
390+
#include <asm/GEN-for-each-reg.h>
391+
#undef GEN
392+
393+
.align 64, 0xcc
394+
SYM_CODE_END(__x86_indirect_its_thunk_array)
395+
396+
#endif
397+
370398
/*
371399
* This function name is magical and is used by -mfunction-return=thunk-extern
372400
* for the compiler to generate JMPs to it.

arch/x86/net/bpf_jit_comp.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,10 @@ static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
661661
{
662662
u8 *prog = *pprog;
663663

664-
if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
664+
if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) {
665+
OPTIMIZER_HIDE_VAR(reg);
666+
emit_jump(&prog, &__x86_indirect_its_thunk_array[reg], ip);
667+
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
665668
EMIT_LFENCE();
666669
EMIT2(0xFF, 0xE0 + reg);
667670
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) {

0 commit comments

Comments
 (0)