Note: needs to be in a section distinct from Retpolines such that the Retpoline RET substitution cannot possibly use immediate jumps. ORC unwinding for zen_untrain_ret() and __x86_return_thunk() is a little tricky but works due to the fact that zen_untrain_ret() doesn't have any stack ops and as such will emit a single ORC entry at the start (+0x3f). Meanwhile, unwinding an IP, including the __x86_return_thunk() one (+0x40) will search for the largest ORC entry smaller or equal to the IP, these will find the one ORC entry (+0x3f) and all works. [ Alexandre: SVM part. ] [ bp: Build fix, massages. ]
Unified · Split
| @@ -96,6 +96,7 @@ SYM_CODE_START(entry_SYSCALL_64) · 95 unmodified lines | |||
| 96 | 96 | ||
| 97 | SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL) | 97 | SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL) |
| 98 | ANNOTATE_NOENDBR | 98 | ANNOTATE_NOENDBR |
| 99 | UNTRAIN_RET | ||
| 99 | 100 | ||
| 100 | /* Construct struct pt_regs on stack */ | 101 | /* Construct struct pt_regs on stack */ |
| 101 | pushq $__USER_DS /* pt_regs->ss */ | 102 | pushq $__USER_DS /* pt_regs->ss */ |
| @@ -717,6 +718,7 @@ native_irq_return_ldt: · 615 unmodified lines | |||
| 717 | pushq %rdi /* Stash user RDI */ | 718 | pushq %rdi /* Stash user RDI */ |
| 718 | swapgs /* to kernel GS */ | 719 | swapgs /* to kernel GS */ |
| 719 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rdi /* to kernel CR3 */ | 720 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rdi /* to kernel CR3 */ |
| 721 | UNTRAIN_RET | ||
| 720 | 722 | ||
| 721 | movq PER_CPU_VAR(espfix_waddr), %rdi | 723 | movq PER_CPU_VAR(espfix_waddr), %rdi |
| 722 | movq %rax, (0*8)(%rdi) /* user RAX */ | 724 | movq %rax, (0*8)(%rdi) /* user RAX */ |
| @@ -911,6 +913,7 @@ SYM_CODE_START_LOCAL(paranoid_entry) · 188 unmodified lines | |||
| 911 | * be retrieved from a kernel internal table. | 913 | * be retrieved from a kernel internal table. |
| 912 | */ | 914 | */ |
| 913 | SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14 | 915 | SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14 |
| 916 | UNTRAIN_RET | ||
| 914 | 917 | ||
| 915 | /* | 918 | /* |
| 916 | * Handling GSBASE depends on the availability of FSGSBASE. | 919 | * Handling GSBASE depends on the availability of FSGSBASE. |
| @@ -1020,6 +1023,7 @@ SYM_CODE_START_LOCAL(error_entry) · 103 unmodified lines | |||
| 1020 | FENCE_SWAPGS_USER_ENTRY | 1023 | FENCE_SWAPGS_USER_ENTRY |
| 1021 | /* We have user CR3. Change to kernel CR3. */ | 1024 | /* We have user CR3. Change to kernel CR3. */ |
| 1022 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rax | 1025 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rax |
| 1026 | UNTRAIN_RET | ||
| 1023 | 1027 | ||
| 1024 | leaq 8(%rsp), %rdi /* arg0 = pt_regs pointer */ | 1028 | leaq 8(%rsp), %rdi /* arg0 = pt_regs pointer */ |
| 1025 | .Lerror_entry_from_usermode_after_swapgs: | 1029 | .Lerror_entry_from_usermode_after_swapgs: |
| @@ -1072,6 +1076,7 @@ SYM_CODE_START_LOCAL(error_entry) · 46 unmodified lines | |||
| 1072 | swapgs | 1076 | swapgs |
| 1073 | FENCE_SWAPGS_USER_ENTRY | 1077 | FENCE_SWAPGS_USER_ENTRY |
| 1074 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rax | 1078 | SWITCH_TO_KERNEL_CR3 scratch_reg=%rax |
| 1079 | UNTRAIN_RET | ||
| 1075 | 1080 | ||
| 1076 | /* | 1081 | /* |
| 1077 | * Pretend that the exception came from user mode: set up pt_regs | 1082 | * Pretend that the exception came from user mode: set up pt_regs |
| @@ -1167,6 +1172,7 @@ SYM_CODE_START(asm_exc_nmi) · 89 unmodified lines | |||
| 1167 | movq %rsp, %rdx | 1172 | movq %rsp, %rdx |
| 1168 | movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp | 1173 | movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp |
| 1169 | UNWIND_HINT_IRET_REGS base=%rdx offset=8 | 1174 | UNWIND_HINT_IRET_REGS base=%rdx offset=8 |
| 1175 | UNTRAIN_RET | ||
| 1170 | pushq 5*8(%rdx) /* pt_regs->ss */ | 1176 | pushq 5*8(%rdx) /* pt_regs->ss */ |
| 1171 | pushq 4*8(%rdx) /* pt_regs->rsp */ | 1177 | pushq 4*8(%rdx) /* pt_regs->rsp */ |
| 1172 | pushq 3*8(%rdx) /* pt_regs->flags */ | 1178 | pushq 3*8(%rdx) /* pt_regs->flags */ |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | |||
| 14 | #include <asm/irqflags.h> | 14 | #include <asm/irqflags.h> |
| 15 | #include <asm/asm.h> | 15 | #include <asm/asm.h> |
| 16 | #include <asm/smap.h> | 16 | #include <asm/smap.h> |
| 17 | #include <asm/nospec-branch.h> | ||
| 17 | #include <linux/linkage.h> | 18 | #include <linux/linkage.h> |
| 18 | #include <linux/err.h> | 19 | #include <linux/err.h> |
| 19 | 20 | ||
| @@ -72,6 +73,7 @@ SYM_CODE_START(entry_SYSENTER_compat) · 52 unmodified lines | |||
| 72 | pushq $__USER32_CS /* pt_regs->cs */ | 73 | pushq $__USER32_CS /* pt_regs->cs */ |
| 73 | pushq $0 /* pt_regs->ip = 0 (placeholder) */ | 74 | pushq $0 /* pt_regs->ip = 0 (placeholder) */ |
| 74 | SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL) | 75 | SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL) |
| 76 | UNTRAIN_RET | ||
| 75 | 77 | ||
| 76 | /* | 78 | /* |
| 77 | * User tracing code (ptrace or signal handlers) might assume that | 79 | * User tracing code (ptrace or signal handlers) might assume that |
| @@ -190,6 +192,7 @@ SYM_CODE_START(entry_SYSCALL_compat) · 112 unmodified lines | |||
| 190 | 192 | ||
| 191 | SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL) | 193 | SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL) |
| 192 | ANNOTATE_NOENDBR | 194 | ANNOTATE_NOENDBR |
| 195 | UNTRAIN_RET | ||
| 193 | 196 | ||
| 194 | /* Construct struct pt_regs on stack */ | 197 | /* Construct struct pt_regs on stack */ |
| 195 | pushq $__USER32_DS /* pt_regs->ss */ | 198 | pushq $__USER32_DS /* pt_regs->ss */ |
| @@ -332,6 +335,7 @@ SYM_CODE_START(entry_INT80_compat) · 136 unmodified lines | |||
| 332 | pushq 0*8(%rax) /* regs->orig_ax */ | 335 | pushq 0*8(%rax) /* regs->orig_ax */ |
| 333 | .Lint80_keep_stack: | 336 | .Lint80_keep_stack: |
| 334 | 337 | ||
| 338 | UNTRAIN_RET | ||
| 335 | PUSH_AND_CLEAR_REGS rax=$-ENOSYS | 339 | PUSH_AND_CLEAR_REGS rax=$-ENOSYS |
| 336 | UNWIND_HINT_REGS | 340 | UNWIND_HINT_REGS |
| 337 | 341 | ||
| @@ -301,6 +301,7 @@ · 300 unmodified lines | |||
| 301 | #define X86_FEATURE_RETPOLINE (11*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */ | 301 | #define X86_FEATURE_RETPOLINE (11*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */ |
| 302 | #define X86_FEATURE_RETPOLINE_LFENCE (11*32+13) /* "" Use LFENCE for Spectre variant 2 */ | 302 | #define X86_FEATURE_RETPOLINE_LFENCE (11*32+13) /* "" Use LFENCE for Spectre variant 2 */ |
| 303 | #define X86_FEATURE_RETHUNK (11*32+14) /* "" Use REturn THUNK */ | 303 | #define X86_FEATURE_RETHUNK (11*32+14) /* "" Use REturn THUNK */ |
| 304 | #define X86_FEATURE_UNRET (11*32+15) /* "" AMD BTB untrain return */ | ||
| 304 | 305 | ||
| 305 | /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ | 306 | /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ |
| 306 | #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ | 307 | #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ |
| @@ -55,7 +55,8 @@ · 54 unmodified lines | |||
| 55 | #else | 55 | #else |
| 56 | # define DISABLE_RETPOLINE ((1 << (X86_FEATURE_RETPOLINE & 31)) | \ | 56 | # define DISABLE_RETPOLINE ((1 << (X86_FEATURE_RETPOLINE & 31)) | \ |
| 57 | (1 << (X86_FEATURE_RETPOLINE_LFENCE & 31)) | \ | 57 | (1 << (X86_FEATURE_RETPOLINE_LFENCE & 31)) | \ |
| 58 | (1 << (X86_FEATURE_RETHUNK & 31))) | 58 | (1 << (X86_FEATURE_RETHUNK & 31)) | \ |
| 59 | (1 << (X86_FEATURE_UNRET & 31))) | ||
| 59 | #endif | 60 | #endif |
| 60 | 61 | ||
| 61 | #ifdef CONFIG_INTEL_IOMMU_SVM | 62 | #ifdef CONFIG_INTEL_IOMMU_SVM |
| @@ -112,6 +112,22 @@ · 111 unmodified lines | |||
| 112 | #endif | 112 | #endif |
| 113 | .endm | 113 | .endm |
| 114 | 114 | ||
| 115 | /* | ||
| 116 | * Mitigate RETBleed for AMD/Hygon Zen uarch. Requires KERNEL CR3 because the | ||
| 117 | * return thunk isn't mapped into the userspace tables (then again, AMD | ||
| 118 | * typically has NO_MELTDOWN). | ||
| 119 | * | ||
| 120 | * Doesn't clobber any registers but does require a stable stack. | ||
| 121 | * | ||
| 122 | * As such, this must be placed after every *SWITCH_TO_KERNEL_CR3 at a point | ||
| 123 | * where we have a stack but before any RET instruction. | ||
| 124 | */ | ||
| 125 | .macro UNTRAIN_RET | ||
| 126 | #ifdef CONFIG_RETPOLINE | ||
| 127 | ALTERNATIVE "", "call zen_untrain_ret", X86_FEATURE_UNRET | ||
| 128 | #endif | ||
| 129 | .endm | ||
| 130 | |||
| 115 | #else /* __ASSEMBLY__ */ | 131 | #else /* __ASSEMBLY__ */ |
| 116 | 132 | ||
| 117 | #define ANNOTATE_RETPOLINE_SAFE \ | 133 | #define ANNOTATE_RETPOLINE_SAFE \ |
| @@ -124,6 +140,7 @@ typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE]; · 6 unmodified lines | |||
| 124 | extern retpoline_thunk_t __x86_indirect_thunk_array[]; | 140 | extern retpoline_thunk_t __x86_indirect_thunk_array[]; |
| 125 | 141 | ||
| 126 | extern void __x86_return_thunk(void); | 142 | extern void __x86_return_thunk(void); |
| 143 | extern void zen_untrain_ret(void); | ||
| 127 | 144 | ||
| 128 | #ifdef CONFIG_RETPOLINE | 145 | #ifdef CONFIG_RETPOLINE |
| 129 | 146 | ||
| @@ -141,7 +141,7 @@ SECTIONS · 140 unmodified lines | |||
| 141 | 141 | ||
| 142 | #ifdef CONFIG_RETPOLINE | 142 | #ifdef CONFIG_RETPOLINE |
| 143 | __indirect_thunk_start = .; | 143 | __indirect_thunk_start = .; |
| 144 | *(.text.__x86.indirect_thunk) | 144 | *(.text.__x86.*) |
| 145 | __indirect_thunk_end = .; | 145 | __indirect_thunk_end = .; |
| 146 | #endif | 146 | #endif |
| 147 | } :text =0xcccc | 147 | } :text =0xcccc |
| @@ -110,6 +110,15 @@ SYM_FUNC_START(__svm_vcpu_run) · 109 unmodified lines | |||
| 110 | mov %r15, VCPU_R15(%_ASM_AX) | 110 | mov %r15, VCPU_R15(%_ASM_AX) |
| 111 | #endif | 111 | #endif |
| 112 | 112 | ||
| 113 | /* | ||
| 114 | * Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be | ||
| 115 | * untrained as soon as we exit the VM and are back to the | ||
| 116 | * kernel. This should be done before re-enabling interrupts | ||
| 117 | * because interrupt handlers won't sanitize 'ret' if the return is | ||
| 118 | * from the kernel. | ||
| 119 | */ | ||
| 120 | UNTRAIN_RET | ||
| 121 | |||
| 113 | /* | 122 | /* |
| 114 | * Clear all general purpose registers except RSP and RAX to prevent | 123 | * Clear all general purpose registers except RSP and RAX to prevent |
| 115 | * speculative use of the guest's values, even those that are reloaded | 124 | * speculative use of the guest's values, even those that are reloaded |
| @@ -190,6 +199,15 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) · 74 unmodified lines | |||
| 190 | FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE | 199 | FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE |
| 191 | #endif | 200 | #endif |
| 192 | 201 | ||
| 202 | /* | ||
| 203 | * Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be | ||
| 204 | * untrained as soon as we exit the VM and are back to the | ||
| 205 | * kernel. This should be done before re-enabling interrupts | ||
| 206 | * because interrupt handlers won't sanitize RET if the return is | ||
| 207 | * from the kernel. | ||
| 208 | */ | ||
| 209 | UNTRAIN_RET | ||
| 210 | |||
| 193 | pop %_ASM_BX | 211 | pop %_ASM_BX |
| 194 | 212 | ||
| 195 | #ifdef CONFIG_X86_64 | 213 | #ifdef CONFIG_X86_64 |
| @@ -72,11 +72,67 @@ SYM_CODE_END(__x86_indirect_thunk_array) · 71 unmodified lines | |||
| 72 | * This function name is magical and is used by -mfunction-return=thunk-extern | 72 | * This function name is magical and is used by -mfunction-return=thunk-extern |
| 73 | * for the compiler to generate JMPs to it. | 73 | * for the compiler to generate JMPs to it. |
| 74 | */ | 74 | */ |
| 75 | SYM_CODE_START(__x86_return_thunk) | 75 | .section .text.__x86.return_thunk |
| 76 | UNWIND_HINT_EMPTY | 76 | |
| 77 | ANNOTATE_NOENDBR | 77 | /* |
| 78 | * Safety details here pertain to the AMD Zen{1,2} microarchitecture: | ||
| 79 | * 1) The RET at __x86_return_thunk must be on a 64 byte boundary, for | ||
| 80 | * alignment within the BTB. | ||
| 81 | * 2) The instruction at zen_untrain_ret must contain, and not | ||
| 82 | * end with, the 0xc3 byte of the RET. | ||
| 83 | * 3) STIBP must be enabled, or SMT disabled, to prevent the sibling thread | ||
| 84 | * from re-poisioning the BTB prediction. | ||
| 85 | */ | ||
| 86 | .align 64 | ||
| 87 | .skip 63, 0xcc | ||
| 88 | SYM_FUNC_START_NOALIGN(zen_untrain_ret); | ||
| 89 | |||
| 90 | /* | ||
| 91 | * As executed from zen_untrain_ret, this is: | ||
| 92 | * | ||
| 93 | * TEST $0xcc, %bl | ||
| 94 | * LFENCE | ||
| 95 | * JMP __x86_return_thunk | ||
| 96 | * | ||
| 97 | * Executing the TEST instruction has a side effect of evicting any BTB | ||
| 98 | * prediction (potentially attacker controlled) attached to the RET, as | ||
| 99 | * __x86_return_thunk + 1 isn't an instruction boundary at the moment. | ||
| 100 | */ | ||
| 101 | .byte 0xf6 | ||
| 102 | |||
| 103 | /* | ||
| 104 | * As executed from __x86_return_thunk, this is a plain RET. | ||
| 105 | * | ||
| 106 | * As part of the TEST above, RET is the ModRM byte, and INT3 the imm8. | ||
| 107 | * | ||
| 108 | * We subsequently jump backwards and architecturally execute the RET. | ||
| 109 | * This creates a correct BTB prediction (type=ret), but in the | ||
| 110 | * meantime we suffer Straight Line Speculation (because the type was | ||
| 111 | * no branch) which is halted by the INT3. | ||
| 112 | * | ||
| 113 | * With SMT enabled and STIBP active, a sibling thread cannot poison | ||
| 114 | * RET's prediction to a type of its choice, but can evict the | ||
| 115 | * prediction due to competitive sharing. If the prediction is | ||
| 116 | * evicted, __x86_return_thunk will suffer Straight Line Speculation | ||
| 117 | * which will be contained safely by the INT3. | ||
| 118 | */ | ||
| 119 | SYM_INNER_LABEL(__x86_return_thunk, SYM_L_GLOBAL) | ||
| 78 | ret | 120 | ret |
| 79 | int3 | 121 | int3 |
| 80 | SYM_CODE_END(__x86_return_thunk) | 122 | SYM_CODE_END(__x86_return_thunk) |
| 81 | 123 | ||
| 82 | __EXPORT_THUNK(__x86_return_thunk) | 124 | /* |
| 125 | * Ensure the TEST decoding / BTB invalidation is complete. | ||
| 126 | */ | ||
| 127 | lfence | ||
| 128 | |||
| 129 | /* | ||
| 130 | * Jump back and execute the RET in the middle of the TEST instruction. | ||
| 131 | * INT3 is for SLS protection. | ||
| 132 | */ | ||
| 133 | jmp __x86_return_thunk | ||
| 134 | int3 | ||
| 135 | SYM_FUNC_END(zen_untrain_ret) | ||
| 136 | __EXPORT_THUNK(zen_untrain_ret) | ||
| 137 | |||
| 138 | EXPORT_SYMBOL(__x86_return_thunk) | ||
| @@ -1302,7 +1302,7 @@ static void add_retpoline_call(struct objtool_file *file, struct instruction *in · 1301 unmodified lines | |||
| 1302 | annotate_call_site(file, insn, false); | 1302 | annotate_call_site(file, insn, false); |
| 1303 | } | 1303 | } |
| 1304 | 1304 | ||
| 1305 | static void add_return_call(struct objtool_file *file, struct instruction *insn) | 1305 | static void add_return_call(struct objtool_file *file, struct instruction *insn, bool add) |
| 1306 | { | 1306 | { |
| 1307 | /* | 1307 | /* |
| 1308 | * Return thunk tail calls are really just returns in disguise, | 1308 | * Return thunk tail calls are really just returns in disguise, |
| @@ -1311,7 +1311,8 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn) · 2 unmodified lines | |||
| 1311 | insn->type = INSN_RETURN; | 1311 | insn->type = INSN_RETURN; |
| 1312 | insn->retpoline_safe = true; | 1312 | insn->retpoline_safe = true; |
| 1313 | 1313 | ||
| 1314 | list_add_tail(&insn->call_node, &file->return_thunk_list); | 1314 | if (add) |
| 1315 | list_add_tail(&insn->call_node, &file->return_thunk_list); | ||
| 1315 | } | 1316 | } |
| 1316 | 1317 | ||
| 1317 | static bool same_function(struct instruction *insn1, struct instruction *insn2) | 1318 | static bool same_function(struct instruction *insn1, struct instruction *insn2) |
| @@ -1367,7 +1368,7 @@ static int add_jump_destinations(struct objtool_file *file) · 49 unmodified lines | |||
| 1367 | add_retpoline_call(file, insn); | 1368 | add_retpoline_call(file, insn); |
| 1368 | continue; | 1369 | continue; |
| 1369 | } else if (reloc->sym->return_thunk) { | 1370 | } else if (reloc->sym->return_thunk) { |
| 1370 | add_return_call(file, insn); | 1371 | add_return_call(file, insn, true); |
| 1371 | continue; | 1372 | continue; |
| 1372 | } else if (insn->func) { | 1373 | } else if (insn->func) { |
| 1373 | /* | 1374 | /* |
| @@ -1387,6 +1388,21 @@ static int add_jump_destinations(struct objtool_file *file) · 13 unmodified lines | |||
| 1387 | 1388 | ||
| 1388 | jump_dest = find_insn(file, dest_sec, dest_off); | 1389 | jump_dest = find_insn(file, dest_sec, dest_off); |
| 1389 | if (!jump_dest) { | 1390 | if (!jump_dest) { |
| 1391 | struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off); | ||
| 1392 | |||
| 1393 | /* | ||
| 1394 | * This is a special case for zen_untrain_ret(). | ||
| 1395 | * It jumps to __x86_return_thunk(), but objtool | ||
| 1396 | * can't find the thunk's starting RET | ||
| 1397 | * instruction, because the RET is also in the | ||
| 1398 | * middle of another instruction. Objtool only | ||
| 1399 | * knows about the outer instruction. | ||
| 1400 | */ | ||
| 1401 | if (sym && sym->return_thunk) { | ||
| 1402 | add_return_call(file, insn, false); | ||
| 1403 | continue; | ||
| 1404 | } | ||
| 1405 | |||
| 1390 | WARN_FUNC("can't find jump dest instruction at %s+0x%lx", | 1406 | WARN_FUNC("can't find jump dest instruction at %s+0x%lx", |
| 1391 | insn->sec, insn->offset, dest_sec->name, | 1407 | insn->sec, insn->offset, dest_sec->name, |
| 1392 | dest_off); | 1408 | dest_off); |