Skip to content

Commit 5e7d4f2

Browse files
pa1guptagregkh
authored andcommitted
x86/its: Add support for ITS-safe indirect thunk
commit 8754e67ad4ac692c67ff1f99c0d07156f04ae40c 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 0eda20c commit 5e7d4f2

File tree

7 files changed

+132
-1
lines changed

7 files changed

+132
-1
lines changed

arch/x86/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,17 @@ config MITIGATION_SPECTRE_BHI
25752575
indirect branches.
25762576
See <file:Documentation/admin-guide/hw-vuln/spectre.rst>
25772577

2578+
config MITIGATION_ITS
2579+
bool "Enable Indirect Target Selection mitigation"
2580+
depends on CPU_SUP_INTEL && X86_64
2581+
depends on RETPOLINE && RETHUNK
2582+
default y
2583+
help
2584+
Enable Indirect Target Selection (ITS) mitigation. ITS is a bug in
2585+
BPU on some Intel CPUs that may allow Spectre V2 style attacks. If
2586+
disabled, mitigation cannot be enabled via cmdline.
2587+
See <file:Documentation/admin-guide/hw-vuln/indirect-target-selection.rst>
2588+
25782589
endif
25792590

25802591
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
@@ -445,6 +445,7 @@
445445
#define X86_FEATURE_BHI_CTRL (21*32+ 2) /* "" BHI_DIS_S HW control available */
446446
#define X86_FEATURE_CLEAR_BHB_HW (21*32+ 3) /* "" BHI_DIS_S HW control enabled */
447447
#define X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT (21*32+ 4) /* "" Clear branch history at vmexit using SW loop */
448+
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32 + 5) /* "" Use thunk for indirect branches in lower half of cacheline */
448449

449450
/*
450451
* BUG word(s)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,12 @@
244244
_ASM_PTR " 999b\n\t" \
245245
".popsection\n\t"
246246

247+
#define ITS_THUNK_SIZE 64
248+
247249
typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE];
250+
typedef u8 its_thunk_t[ITS_THUNK_SIZE];
248251
extern retpoline_thunk_t __x86_indirect_thunk_array[];
252+
extern its_thunk_t __x86_indirect_its_thunk_array[];
249253

250254
#ifdef CONFIG_RETHUNK
251255
extern void __x86_return_thunk(void);

arch/x86/kernel/alternative.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,74 @@ static int emit_indirect(int op, int reg, u8 *bytes)
396396
return i;
397397
}
398398

399+
#ifdef CONFIG_MITIGATION_ITS
400+
401+
static int __emit_trampoline(void *addr, struct insn *insn, u8 *bytes,
402+
void *call_dest, void *jmp_dest)
403+
{
404+
u8 op = insn->opcode.bytes[0];
405+
int i = 0;
406+
407+
/*
408+
* Clang does 'weird' Jcc __x86_indirect_thunk_r11 conditional
409+
* tail-calls. Deal with them.
410+
*/
411+
if (is_jcc32(insn)) {
412+
bytes[i++] = op;
413+
op = insn->opcode.bytes[1];
414+
goto clang_jcc;
415+
}
416+
417+
if (insn->length == 6)
418+
bytes[i++] = 0x2e; /* CS-prefix */
419+
420+
switch (op) {
421+
case CALL_INSN_OPCODE:
422+
__text_gen_insn(bytes+i, op, addr+i,
423+
call_dest,
424+
CALL_INSN_SIZE);
425+
i += CALL_INSN_SIZE;
426+
break;
427+
428+
case JMP32_INSN_OPCODE:
429+
clang_jcc:
430+
__text_gen_insn(bytes+i, op, addr+i,
431+
jmp_dest,
432+
JMP32_INSN_SIZE);
433+
i += JMP32_INSN_SIZE;
434+
break;
435+
436+
default:
437+
WARN(1, "%pS %px %*ph\n", addr, addr, 6, addr);
438+
return -1;
439+
}
440+
441+
WARN_ON_ONCE(i != insn->length);
442+
443+
return i;
444+
}
445+
446+
static int emit_its_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes)
447+
{
448+
return __emit_trampoline(addr, insn, bytes,
449+
__x86_indirect_its_thunk_array[reg],
450+
__x86_indirect_its_thunk_array[reg]);
451+
}
452+
453+
/* Check if an indirect branch is at ITS-unsafe address */
454+
static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg)
455+
{
456+
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
457+
return false;
458+
459+
/* Indirect branch opcode is 2 or 3 bytes depending on reg */
460+
addr += 1 + reg / 8;
461+
462+
/* Lower-half of the cacheline? */
463+
return !(addr & 0x20);
464+
}
465+
#endif
466+
399467
/*
400468
* Rewrite the compiler generated retpoline thunk calls.
401469
*
@@ -466,6 +534,15 @@ static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes)
466534
bytes[i++] = 0xe8; /* LFENCE */
467535
}
468536

537+
#ifdef CONFIG_MITIGATION_ITS
538+
/*
539+
* Check if the address of last byte of emitted-indirect is in
540+
* lower-half of the cacheline. Such branches need ITS mitigation.
541+
*/
542+
if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + i, reg))
543+
return emit_its_trampoline(addr, insn, reg, bytes);
544+
#endif
545+
469546
ret = emit_indirect(op, reg, bytes + i);
470547
if (ret < 0)
471548
return ret;

arch/x86/kernel/vmlinux.lds.S

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,12 @@ INIT_PER_CPU(irq_stack_backing_store);
528528
"SRSO function pair won't alias");
529529
#endif
530530

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

533539
#ifdef CONFIG_KEXEC_CORE

arch/x86/lib/retpoline.S

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,34 @@ SYM_FUNC_START(entry_untrain_ret)
258258
SYM_FUNC_END(entry_untrain_ret)
259259
__EXPORT_THUNK(entry_untrain_ret)
260260

261+
#ifdef CONFIG_MITIGATION_ITS
262+
263+
.macro ITS_THUNK reg
264+
265+
SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL)
266+
UNWIND_HINT_EMPTY
267+
ANNOTATE_NOENDBR
268+
ANNOTATE_RETPOLINE_SAFE
269+
jmp *%\reg
270+
int3
271+
.align 32, 0xcc /* fill to the end of the line */
272+
.skip 32, 0xcc /* skip to the next upper half */
273+
.endm
274+
275+
/* ITS mitigation requires thunks be aligned to upper half of cacheline */
276+
.align 64, 0xcc
277+
.skip 32, 0xcc
278+
SYM_CODE_START(__x86_indirect_its_thunk_array)
279+
280+
#define GEN(reg) ITS_THUNK reg
281+
#include <asm/GEN-for-each-reg.h>
282+
#undef GEN
283+
284+
.align 64, 0xcc
285+
SYM_CODE_END(__x86_indirect_its_thunk_array)
286+
287+
#endif
288+
261289
SYM_CODE_START(__x86_return_thunk)
262290
UNWIND_HINT_FUNC
263291
ANNOTATE_NOENDBR

arch/x86/net/bpf_jit_comp.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,11 @@ static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
464464
{
465465
u8 *prog = *pprog;
466466

467-
if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
467+
if (IS_ENABLED(CONFIG_MITIGATION_ITS) &&
468+
cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) {
469+
OPTIMIZER_HIDE_VAR(reg);
470+
emit_jump(&prog, &__x86_indirect_its_thunk_array[reg], ip);
471+
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
468472
EMIT_LFENCE();
469473
EMIT2(0xFF, 0xE0 + reg);
470474
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) {

0 commit comments

Comments
 (0)