Skip to content

Commit 16a7d5b

Browse files
pa1guptagregkh
authored andcommitted
x86/its: Add support for ITS-safe indirect thunk
commit 8754e67 upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a6f2a43 commit 16a7d5b

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
@@ -2747,6 +2747,17 @@ config MITIGATION_SSB
27472747
of speculative execution in a similar way to the Meltdown and Spectre
27482748
security vulnerabilities.
27492749

2750+
config MITIGATION_ITS
2751+
bool "Enable Indirect Target Selection mitigation"
2752+
depends on CPU_SUP_INTEL && X86_64
2753+
depends on MITIGATION_RETPOLINE && MITIGATION_RETHUNK
2754+
default y
2755+
help
2756+
Enable Indirect Target Selection (ITS) mitigation. ITS is a bug in
2757+
BPU on some Intel CPUs that may allow Spectre V2 style attacks. If
2758+
disabled, mitigation cannot be enabled via cmdline.
2759+
See <file:Documentation/admin-guide/hw-vuln/indirect-target-selection.rst>
2760+
27502761
endif
27512762

27522763
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
@@ -475,6 +475,7 @@
475475
#define X86_FEATURE_CLEAR_BHB_HW (21*32+ 3) /* BHI_DIS_S HW control enabled */
476476
#define X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT (21*32+ 4) /* Clear branch history at vmexit using SW loop */
477477
#define X86_FEATURE_FAST_CPPC (21*32 + 5) /* AMD Fast CPPC */
478+
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32 + 6) /* Use thunk for indirect branches in lower half of cacheline */
478479

479480
/*
480481
* BUG word(s)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,14 @@
355355
".long 999b\n\t" \
356356
".popsection\n\t"
357357

358+
#define ITS_THUNK_SIZE 64
359+
358360
typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE];
361+
typedef u8 its_thunk_t[ITS_THUNK_SIZE];
359362
extern retpoline_thunk_t __x86_indirect_thunk_array[];
360363
extern retpoline_thunk_t __x86_indirect_call_thunk_array[];
361364
extern retpoline_thunk_t __x86_indirect_jump_thunk_array[];
365+
extern its_thunk_t __x86_indirect_its_thunk_array[];
362366

363367
#ifdef CONFIG_MITIGATION_RETHUNK
364368
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
@@ -530,4 +530,10 @@ INIT_PER_CPU(irq_stack_backing_store);
530530
"SRSO function pair won't alias");
531531
#endif
532532

533+
#if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
534+
. = ASSERT(__x86_indirect_its_thunk_rax & 0x20, "__x86_indirect_thunk_rax not in second half of cacheline");
535+
. = ASSERT(((__x86_indirect_its_thunk_rcx - __x86_indirect_its_thunk_rax) % 64) == 0, "Indirect thunks are not cacheline apart");
536+
. = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array");
537+
#endif
538+
533539
#endif /* CONFIG_X86_64 */

arch/x86/lib/retpoline.S

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

367367
#endif /* CONFIG_MITIGATION_CALL_DEPTH_TRACKING */
368368

369+
#ifdef CONFIG_MITIGATION_ITS
370+
371+
.macro ITS_THUNK reg
372+
373+
SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL)
374+
UNWIND_HINT_UNDEFINED
375+
ANNOTATE_NOENDBR
376+
ANNOTATE_RETPOLINE_SAFE
377+
jmp *%\reg
378+
int3
379+
.align 32, 0xcc /* fill to the end of the line */
380+
.skip 32, 0xcc /* skip to the next upper half */
381+
.endm
382+
383+
/* ITS mitigation requires thunks be aligned to upper half of cacheline */
384+
.align 64, 0xcc
385+
.skip 32, 0xcc
386+
SYM_CODE_START(__x86_indirect_its_thunk_array)
387+
388+
#define GEN(reg) ITS_THUNK reg
389+
#include <asm/GEN-for-each-reg.h>
390+
#undef GEN
391+
392+
.align 64, 0xcc
393+
SYM_CODE_END(__x86_indirect_its_thunk_array)
394+
395+
#endif
396+
369397
/*
370398
* This function name is magical and is used by -mfunction-return=thunk-extern
371399
* 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
@@ -639,7 +639,10 @@ static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
639639
{
640640
u8 *prog = *pprog;
641641

642-
if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
642+
if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) {
643+
OPTIMIZER_HIDE_VAR(reg);
644+
emit_jump(&prog, &__x86_indirect_its_thunk_array[reg], ip);
645+
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
643646
EMIT_LFENCE();
644647
EMIT2(0xFF, 0xE0 + reg);
645648
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) {

0 commit comments

Comments
 (0)