Skip to content

Commit bfe7f99

Browse files
bp3tk0vgregkh
authored andcommitted
x86/bugs: Make Safe-RET robust against interrupt injection
commit 7e7f81c upstream. An attacker injecting interrupts while the Safe-RET mitigation executes on machines affected by SRSO can neutralize the safe return sequence, potentially leading to data leakage through speculative execution. Fixup register state as if the Safe-RET sequence executed successfully by "emulating" it, in a manner of speaking, and avoid executing a RET instruction after returning from the interrupt. Co-developed-by: David Kaplan <David.Kaplan@amd.com> Signed-off-by: David Kaplan <David.Kaplan@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 856a9b5 commit bfe7f99

4 files changed

Lines changed: 123 additions & 1 deletion

File tree

arch/x86/entry/entry_64.S

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,8 @@ SYM_CODE_START(paranoid_entry)
937937
IBRS_ENTER save_reg=%r15
938938
UNTRAIN_RET_FROM_CALL
939939

940+
HANDLE_INTR_SAFERET 8(%rsp)
941+
940942
RET
941943
SYM_CODE_END(paranoid_entry)
942944

@@ -1039,6 +1041,11 @@ SYM_CODE_START(error_entry)
10391041
movl %ecx, %eax /* zero extend */
10401042
cmpq %rax, RIP+8(%rsp)
10411043
je .Lbstep_iret
1044+
1045+
VALIDATE_UNRET_END
1046+
1047+
HANDLE_INTR_SAFERET 8(%rsp)
1048+
10421049
cmpq $.Lgs_change, RIP+8(%rsp)
10431050
jne .Lerror_entry_done_lfence
10441051

@@ -1057,7 +1064,6 @@ SYM_CODE_START(error_entry)
10571064
FENCE_SWAPGS_KERNEL_ENTRY
10581065
CALL_DEPTH_ACCOUNT
10591066
leaq 8(%rsp), %rax /* return pt_regs pointer */
1060-
VALIDATE_UNRET_END
10611067
RET
10621068

10631069
.Lbstep_iret:

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <asm/msr-index.h>
1313
#include <asm/unwind_hints.h>
1414
#include <asm/percpu.h>
15+
#include <asm/ptrace-abi.h>
1516

1617
/*
1718
* Call depth tracking for Intel SKL CPUs to address the RSB underflow
@@ -176,6 +177,50 @@
176177
add $(BITS_PER_LONG/8), %_ASM_SP; \
177178
lfence;
178179

180+
/*
181+
* Helper for detecting if an interrupt occurred at an unsafe location within
182+
* Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get
183+
* poisoned by the interrupt handler.
184+
*
185+
* The Safe-RET sequence is:
186+
*
187+
* CALL
188+
* LEA 8(%RSP), %RSP
189+
* RET
190+
*
191+
* The two CMPs below check whether RIP points to after the CALL or after the
192+
* LEA.
193+
*
194+
* The LFENCE below is to address this particular speculation case:
195+
*
196+
* 1. Userspace runs and poisons the BTB around the safe-RET routine
197+
*
198+
* 2. Userspace triggers some kind of exception
199+
*
200+
* 3. Kernel executes error_entry() and mis-speculates the branch into thinking
201+
* it actually came from kernel space
202+
*
203+
* 4. The kernel then further mis-speculates that the exception occurred due
204+
* to an interrupted safe-RET
205+
*
206+
* 5. The handle_interrupted_saferet() routine speculatively executes and
207+
* speculatively does a safe-RET. But this is unsafe since it was never
208+
* untrained.
209+
*
210+
* The LFENCE fixes this by ensuring step 5 is never reached speculatively.
211+
* Note that this LFENCE only occurs if safe-RET was actually interrupted (so
212+
* it's outside of the normal path).
213+
*/
214+
#define __HANDLE_INTR_SAFERET(name, pt_regs) \
215+
cmpq $(name), RIP+pt_regs; \
216+
jb 1f; \
217+
cmpq $(name)+5, RIP+pt_regs; \
218+
ja 1f; \
219+
lfence; \
220+
leaq pt_regs, %rdi; \
221+
call handle_interrupted_saferet; \
222+
1:
223+
179224
#ifdef __ASSEMBLER__
180225

181226
/*
@@ -293,6 +338,14 @@
293338
#define UNTRAIN_RET_FROM_CALL \
294339
__UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL)
295340

341+
.macro HANDLE_INTR_SAFERET pt_regs
342+
#ifdef CONFIG_MITIGATION_SRSO
343+
ALTERNATIVE_2 "", \
344+
__stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \
345+
__stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS
346+
347+
#endif
348+
.endm
296349

297350
.macro CALL_DEPTH_ACCOUNT
298351
#ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING
@@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void)
625678
x86_clear_cpu_buffers();
626679
}
627680

681+
void srso_safe_ret(void);
682+
void srso_alias_safe_ret(void);
683+
void handle_interrupted_saferet(struct pt_regs *regs);
684+
628685
#endif /* __ASSEMBLER__ */
629686

630687
#endif /* _ASM_X86_NOSPEC_BRANCH_H_ */

arch/x86/kernel/cpu/bugs.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3829,3 +3829,42 @@ void __warn_thunk(void)
38293829
{
38303830
WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n");
38313831
}
3832+
3833+
#ifdef CONFIG_MITIGATION_SRSO
3834+
/*
3835+
* Called during exception/interrupt entry if interrupted during the
3836+
* safe-RET sequence. The safe-RET sequence consists of 3 instructions:
3837+
*
3838+
* CALL
3839+
* LEA 8(%RSP), %RSP
3840+
* RET
3841+
*
3842+
* An interrupt after the CALL or after the LEA could potentially lead
3843+
* to branch predictor poisoning and results in the sequence not being
3844+
* able to be safely resumed.
3845+
*
3846+
* Therefore, modify the regs state as if the remaining part of the
3847+
* safe-RET sequence executed so the interrupt returns back to the
3848+
* desired return target, instead of the to the safe-RET sequence.
3849+
*/
3850+
void noinstr handle_interrupted_saferet(struct pt_regs *regs)
3851+
{
3852+
unsigned long rip = regs->ip;
3853+
3854+
if (rip == (unsigned long) srso_safe_ret ||
3855+
rip == (unsigned long) srso_alias_safe_ret) {
3856+
/* Modify stack pointer as if LEA executed: */
3857+
regs->sp += 8;
3858+
}
3859+
3860+
/*
3861+
* Adjust registers as if RET executed:
3862+
*
3863+
* 1. Read the return address off the stack and into rIP:
3864+
*/
3865+
regs->ip = *(unsigned long *)(regs->sp);
3866+
3867+
/* 2. Pop rIP off the stack: */
3868+
regs->sp += 8;
3869+
}
3870+
#endif /* CONFIG_MITIGATION_SRSO */

arch/x86/lib/retpoline.S

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret)
207207

208208
.pushsection .text..__x86.rethunk_safe
209209
SYM_CODE_START_NOALIGN(srso_alias_safe_ret)
210+
211+
/*
212+
* Tell objtool that those are not function pointers referenced by
213+
* __HANDLE_INTR_SAFERET(). Below too.
214+
*/
215+
ANNOTATE_NOENDBR
216+
217+
/*
218+
* Safe-RET sequence. If you need to change it, adjust
219+
* handle_interrupted_saferet() too.
220+
*/
210221
lea 8(%_ASM_SP), %_ASM_SP
211222
UNWIND_HINT_FUNC
223+
224+
ANNOTATE_NOENDBR
212225
ANNOTATE_UNRET_SAFE
213226
ret
227+
/* End of Safe-RET sequence */
214228
int3
215229
SYM_FUNC_END(srso_alias_safe_ret)
216230

@@ -245,8 +259,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret)
245259
* the stack.
246260
*/
247261
SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL)
262+
/*
263+
* Safe-RET sequence. If you need to change it, adjust
264+
* handle_interrupted_saferet() too.
265+
*/
248266
lea 8(%_ASM_SP), %_ASM_SP
249267
ret
268+
/* End of Safe-RET sequence */
269+
250270
int3
251271
int3
252272
/* end of movabs */

0 commit comments

Comments
 (0)