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

commit a75bf27fe41abe658c53276a0c486c4bf9adecfc upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pawan Gupta
2024-06-21 21:17:21 -07:00
committed by Greg Kroah-Hartman
parent 5e7d4f2ace
commit dbd8f170af
8 changed files with 55 additions and 5 deletions

View File

@@ -81,6 +81,20 @@ extern void apply_ibt_endbr(s32 *start, s32 *end);
struct module; struct module;
#if defined(CONFIG_RETHUNK) && defined(CONFIG_OBJTOOL)
extern bool cpu_wants_rethunk(void);
extern bool cpu_wants_rethunk_at(void *addr);
#else
static __always_inline bool cpu_wants_rethunk(void)
{
return false;
}
static __always_inline bool cpu_wants_rethunk_at(void *addr)
{
return false;
}
#endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern void alternatives_smp_module_add(struct module *mod, char *name, extern void alternatives_smp_module_add(struct module *mod, char *name,
void *locks, void *locks_end, void *locks, void *locks_end,

View File

@@ -257,6 +257,12 @@ extern void __x86_return_thunk(void);
static inline void __x86_return_thunk(void) {} static inline void __x86_return_thunk(void) {}
#endif #endif
#ifdef CONFIG_MITIGATION_ITS
extern void its_return_thunk(void);
#else
static inline void its_return_thunk(void) {}
#endif
extern void retbleed_return_thunk(void); extern void retbleed_return_thunk(void);
extern void srso_return_thunk(void); extern void srso_return_thunk(void);
extern void srso_alias_return_thunk(void); extern void srso_alias_return_thunk(void);

View File

@@ -614,6 +614,21 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
#ifdef CONFIG_RETHUNK #ifdef CONFIG_RETHUNK
bool cpu_wants_rethunk(void)
{
return cpu_feature_enabled(X86_FEATURE_RETHUNK);
}
bool cpu_wants_rethunk_at(void *addr)
{
if (!cpu_feature_enabled(X86_FEATURE_RETHUNK))
return false;
if (x86_return_thunk != its_return_thunk)
return true;
return !((unsigned long)addr & 0x20);
}
/* /*
* Rewrite the compiler generated return thunk tail-calls. * Rewrite the compiler generated return thunk tail-calls.
* *
@@ -629,7 +644,7 @@ static int patch_return(void *addr, struct insn *insn, u8 *bytes)
{ {
int i = 0; int i = 0;
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { if (cpu_wants_rethunk_at(addr)) {
if (x86_return_thunk == __x86_return_thunk) if (x86_return_thunk == __x86_return_thunk)
return -1; return -1;

View File

@@ -360,7 +360,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
goto fail; goto fail;
ip = trampoline + size; ip = trampoline + size;
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) if (cpu_wants_rethunk_at(ip))
__text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE); __text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE);
else else
memcpy(ip, retq, sizeof(retq)); memcpy(ip, retq, sizeof(retq));

View File

@@ -79,7 +79,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
break; break;
case RET: case RET:
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) if (cpu_wants_rethunk_at(insn))
code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk); code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk);
else else
code = &retinsn; code = &retinsn;

View File

@@ -534,6 +534,10 @@ INIT_PER_CPU(irq_stack_backing_store);
. = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array"); . = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array");
#endif #endif
#if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
. = ASSERT(its_return_thunk & 0x20, "its_return_thunk not in second half of cacheline");
#endif
#endif /* CONFIG_X86_64 */ #endif /* CONFIG_X86_64 */
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE

View File

@@ -284,7 +284,18 @@ SYM_CODE_START(__x86_indirect_its_thunk_array)
.align 64, 0xcc .align 64, 0xcc
SYM_CODE_END(__x86_indirect_its_thunk_array) SYM_CODE_END(__x86_indirect_its_thunk_array)
#endif .align 64, 0xcc
.skip 32, 0xcc
SYM_CODE_START(its_return_thunk)
UNWIND_HINT_FUNC
ANNOTATE_NOENDBR
ANNOTATE_UNRET_SAFE
ret
int3
SYM_CODE_END(its_return_thunk)
EXPORT_SYMBOL(its_return_thunk)
#endif /* CONFIG_MITIGATION_ITS */
SYM_CODE_START(__x86_return_thunk) SYM_CODE_START(__x86_return_thunk)
UNWIND_HINT_FUNC UNWIND_HINT_FUNC

View File

@@ -487,7 +487,7 @@ static void emit_return(u8 **pprog, u8 *ip)
{ {
u8 *prog = *pprog; u8 *prog = *pprog;
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { if (cpu_wants_rethunk()) {
emit_jump(&prog, x86_return_thunk, ip); emit_jump(&prog, x86_return_thunk, ip);
} else { } else {
EMIT1(0xC3); /* ret */ EMIT1(0xC3); /* ret */