Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/its: Add support for ITS-safe return thunk

RETs in the lower half of cacheline may be affected by ITS bug,
specifically when the RSB-underflows. Use ITS-safe return thunk for such
RETs.

RETs that are not patched:

- RET in retpoline sequence does not need to be patched, because the
sequence itself fills an RSB before RET.
- RET in Call Depth Tracking (CDT) thunks __x86_indirect_{call|jump}_thunk
and call_depth_return_thunk are not patched because CDT by design
prevents RSB-underflow.
- RETs in .init section are not reachable after init.
- RETs that are explicitly marked safe with ANNOTATE_UNRET_SAFE.

Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>

authored by

Pawan Gupta and committed by
Dave Hansen
a75bf27f 8754e67a

+57 -7
+14
arch/x86/include/asm/alternative.h
··· 124 124 } 125 125 #endif 126 126 127 + #if defined(CONFIG_MITIGATION_RETHUNK) && defined(CONFIG_OBJTOOL) 128 + extern bool cpu_wants_rethunk(void); 129 + extern bool cpu_wants_rethunk_at(void *addr); 130 + #else 131 + static __always_inline bool cpu_wants_rethunk(void) 132 + { 133 + return false; 134 + } 135 + static __always_inline bool cpu_wants_rethunk_at(void *addr) 136 + { 137 + return false; 138 + } 139 + #endif 140 + 127 141 #ifdef CONFIG_SMP 128 142 extern void alternatives_smp_module_add(struct module *mod, char *name, 129 143 void *locks, void *locks_end,
+6
arch/x86/include/asm/nospec-branch.h
··· 367 367 static inline void srso_alias_return_thunk(void) {} 368 368 #endif 369 369 370 + #ifdef CONFIG_MITIGATION_ITS 371 + extern void its_return_thunk(void); 372 + #else 373 + static inline void its_return_thunk(void) {} 374 + #endif 375 + 370 376 extern void retbleed_return_thunk(void); 371 377 extern void srso_return_thunk(void); 372 378 extern void srso_alias_return_thunk(void);
+17 -2
arch/x86/kernel/alternative.c
··· 814 814 815 815 #ifdef CONFIG_MITIGATION_RETHUNK 816 816 817 + bool cpu_wants_rethunk(void) 818 + { 819 + return cpu_feature_enabled(X86_FEATURE_RETHUNK); 820 + } 821 + 822 + bool cpu_wants_rethunk_at(void *addr) 823 + { 824 + if (!cpu_feature_enabled(X86_FEATURE_RETHUNK)) 825 + return false; 826 + if (x86_return_thunk != its_return_thunk) 827 + return true; 828 + 829 + return !((unsigned long)addr & 0x20); 830 + } 831 + 817 832 /* 818 833 * Rewrite the compiler generated return thunk tail-calls. 819 834 * ··· 845 830 int i = 0; 846 831 847 832 /* Patch the custom return thunks... */ 848 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { 833 + if (cpu_wants_rethunk_at(addr)) { 849 834 i = JMP32_INSN_SIZE; 850 835 __text_gen_insn(bytes, JMP32_INSN_OPCODE, addr, x86_return_thunk, i); 851 836 } else { ··· 862 847 { 863 848 s32 *s; 864 849 865 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 850 + if (cpu_wants_rethunk()) 866 851 static_call_force_reinit(); 867 852 868 853 for (s = start; s < end; s++) {
+1 -1
arch/x86/kernel/ftrace.c
··· 354 354 goto fail; 355 355 356 356 ip = trampoline + size; 357 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 357 + if (cpu_wants_rethunk_at(ip)) 358 358 __text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE); 359 359 else 360 360 memcpy(ip, retq, sizeof(retq));
+2 -2
arch/x86/kernel/static_call.c
··· 81 81 break; 82 82 83 83 case RET: 84 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 84 + if (cpu_wants_rethunk_at(insn)) 85 85 code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk); 86 86 else 87 87 code = &retinsn; ··· 90 90 case JCC: 91 91 if (!func) { 92 92 func = __static_call_return; 93 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 93 + if (cpu_wants_rethunk()) 94 94 func = x86_return_thunk; 95 95 } 96 96
+4
arch/x86/kernel/vmlinux.lds.S
··· 503 503 . = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array"); 504 504 #endif 505 505 506 + #if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B) 507 + . = ASSERT(its_return_thunk & 0x20, "its_return_thunk not in second half of cacheline"); 508 + #endif 509 + 506 510 #endif /* CONFIG_X86_64 */ 507 511 508 512 /*
+12 -1
arch/x86/lib/retpoline.S
··· 393 393 .align 64, 0xcc 394 394 SYM_CODE_END(__x86_indirect_its_thunk_array) 395 395 396 - #endif 396 + .align 64, 0xcc 397 + .skip 32, 0xcc 398 + SYM_CODE_START(its_return_thunk) 399 + UNWIND_HINT_FUNC 400 + ANNOTATE_NOENDBR 401 + ANNOTATE_UNRET_SAFE 402 + ret 403 + int3 404 + SYM_CODE_END(its_return_thunk) 405 + EXPORT_SYMBOL(its_return_thunk) 406 + 407 + #endif /* CONFIG_MITIGATION_ITS */ 397 408 398 409 /* 399 410 * This function name is magical and is used by -mfunction-return=thunk-extern
+1 -1
arch/x86/net/bpf_jit_comp.c
··· 686 686 { 687 687 u8 *prog = *pprog; 688 688 689 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { 689 + if (cpu_wants_rethunk()) { 690 690 emit_jump(&prog, x86_return_thunk, ip); 691 691 } else { 692 692 EMIT1(0xC3); /* ret */