|
12 | 12 | #include <asm/msr-index.h> |
13 | 13 | #include <asm/unwind_hints.h> |
14 | 14 | #include <asm/percpu.h> |
| 15 | +#include <asm/ptrace-abi.h> |
15 | 16 |
|
16 | 17 | /* |
17 | 18 | * Call depth tracking for Intel SKL CPUs to address the RSB underflow |
|
176 | 177 | add $(BITS_PER_LONG/8), %_ASM_SP; \ |
177 | 178 | lfence; |
178 | 179 |
|
| 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 | + |
179 | 224 | #ifdef __ASSEMBLER__ |
180 | 225 |
|
181 | 226 | /* |
|
293 | 338 | #define UNTRAIN_RET_FROM_CALL \ |
294 | 339 | __UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL) |
295 | 340 |
|
| 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 |
296 | 349 |
|
297 | 350 | .macro CALL_DEPTH_ACCOUNT |
298 | 351 | #ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING |
@@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void) |
625 | 678 | x86_clear_cpu_buffers(); |
626 | 679 | } |
627 | 680 |
|
| 681 | +void srso_safe_ret(void); |
| 682 | +void srso_alias_safe_ret(void); |
| 683 | +void handle_interrupted_saferet(struct pt_regs *regs); |
| 684 | + |
628 | 685 | #endif /* __ASSEMBLER__ */ |
629 | 686 |
|
630 | 687 | #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */ |
0 commit comments