1 //==- X86IndirectThunks.cpp - Construct indirect call/jump thunks for x86 --=// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// \file 9 /// 10 /// Pass that injects an MI thunk that is used to lower indirect calls in a way 11 /// that prevents speculation on some x86 processors and can be used to mitigate 12 /// security vulnerabilities due to targeted speculative execution and side 13 /// channels such as CVE-2017-5715. 14 /// 15 /// Currently supported thunks include: 16 /// - Retpoline -- A RET-implemented trampoline that lowers indirect calls 17 /// - LVI Thunk -- A CALL/JMP-implemented thunk that forces load serialization 18 /// before making an indirect call/jump 19 /// 20 /// Note that the reason that this is implemented as a MachineFunctionPass and 21 /// not a ModulePass is that ModulePasses at this point in the LLVM X86 pipeline 22 /// serialize all transformations, which can consume lots of memory. 23 /// 24 /// TODO(chandlerc): All of this code could use better comments and 25 /// documentation. 26 /// 27 //===----------------------------------------------------------------------===// 28 29 #include "X86.h" 30 #include "X86InstrBuilder.h" 31 #include "X86Subtarget.h" 32 #include "llvm/CodeGen/IndirectThunks.h" 33 #include "llvm/CodeGen/MachineFunction.h" 34 #include "llvm/CodeGen/MachineFunctionPass.h" 35 #include "llvm/CodeGen/MachineInstrBuilder.h" 36 #include "llvm/CodeGen/MachineModuleInfo.h" 37 #include "llvm/CodeGen/Passes.h" 38 #include "llvm/CodeGen/TargetPassConfig.h" 39 #include "llvm/IR/IRBuilder.h" 40 #include "llvm/IR/Instructions.h" 41 #include "llvm/IR/Module.h" 42 #include "llvm/Support/CommandLine.h" 43 #include "llvm/Support/Debug.h" 44 #include "llvm/Support/raw_ostream.h" 45 #include "llvm/Target/TargetMachine.h" 46 47 using namespace llvm; 48 49 #define DEBUG_TYPE "x86-retpoline-thunks" 50 51 static const char RetpolineNamePrefix[] = "__llvm_retpoline_"; 52 static const char R11RetpolineName[] = "__llvm_retpoline_r11"; 53 static const char EAXRetpolineName[] = "__llvm_retpoline_eax"; 54 static const char ECXRetpolineName[] = "__llvm_retpoline_ecx"; 55 static const char EDXRetpolineName[] = "__llvm_retpoline_edx"; 56 static const char EDIRetpolineName[] = "__llvm_retpoline_edi"; 57 58 static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_"; 59 static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11"; 60 61 namespace { 62 struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> { 63 const char *getThunkPrefix() { return RetpolineNamePrefix; } 64 bool mayUseThunk(const MachineFunction &MF) { 65 const auto &STI = MF.getSubtarget<X86Subtarget>(); 66 return (STI.useRetpolineIndirectCalls() || 67 STI.useRetpolineIndirectBranches()) && 68 !STI.useRetpolineExternalThunk(); 69 } 70 bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, 71 bool ExistingThunks); 72 void populateThunk(MachineFunction &MF); 73 }; 74 75 struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> { 76 const char *getThunkPrefix() { return LVIThunkNamePrefix; } 77 bool mayUseThunk(const MachineFunction &MF) { 78 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity(); 79 } 80 bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, 81 bool ExistingThunks) { 82 if (ExistingThunks) 83 return false; 84 createThunkFunction(MMI, R11LVIThunkName); 85 return true; 86 } 87 void populateThunk(MachineFunction &MF) { 88 assert (MF.size() == 1); 89 MachineBasicBlock *Entry = &MF.front(); 90 Entry->clear(); 91 92 // This code mitigates LVI by replacing each indirect call/jump with a 93 // direct call/jump to a thunk that looks like: 94 // ``` 95 // lfence 96 // jmpq *%r11 97 // ``` 98 // This ensures that if the value in register %r11 was loaded from memory, 99 // then the value in %r11 is (architecturally) correct prior to the jump. 100 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 101 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE)); 102 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11); 103 MF.front().addLiveIn(X86::R11); 104 } 105 }; 106 107 class X86IndirectThunks 108 : public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> { 109 public: 110 static char ID; 111 112 X86IndirectThunks() : ThunkInserterPass(ID) {} 113 114 StringRef getPassName() const override { return "X86 Indirect Thunks"; } 115 }; 116 117 } // end anonymous namespace 118 119 bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI, 120 MachineFunction &MF, 121 bool ExistingThunks) { 122 if (ExistingThunks) 123 return false; 124 if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64) 125 createThunkFunction(MMI, R11RetpolineName); 126 else 127 for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName, 128 EDIRetpolineName}) 129 createThunkFunction(MMI, Name); 130 return true; 131 } 132 133 void RetpolineThunkInserter::populateThunk(MachineFunction &MF) { 134 bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; 135 Register ThunkReg; 136 if (Is64Bit) { 137 assert(MF.getName() == "__llvm_retpoline_r11" && 138 "Should only have an r11 thunk on 64-bit targets"); 139 140 // __llvm_retpoline_r11: 141 // callq .Lr11_call_target 142 // .Lr11_capture_spec: 143 // pause 144 // lfence 145 // jmp .Lr11_capture_spec 146 // .align 16 147 // .Lr11_call_target: 148 // movq %r11, (%rsp) 149 // retq 150 ThunkReg = X86::R11; 151 } else { 152 // For 32-bit targets we need to emit a collection of thunks for various 153 // possible scratch registers as well as a fallback that uses EDI, which is 154 // normally callee saved. 155 // __llvm_retpoline_eax: 156 // calll .Leax_call_target 157 // .Leax_capture_spec: 158 // pause 159 // jmp .Leax_capture_spec 160 // .align 16 161 // .Leax_call_target: 162 // movl %eax, (%esp) # Clobber return addr 163 // retl 164 // 165 // __llvm_retpoline_ecx: 166 // ... # Same setup 167 // movl %ecx, (%esp) 168 // retl 169 // 170 // __llvm_retpoline_edx: 171 // ... # Same setup 172 // movl %edx, (%esp) 173 // retl 174 // 175 // __llvm_retpoline_edi: 176 // ... # Same setup 177 // movl %edi, (%esp) 178 // retl 179 if (MF.getName() == EAXRetpolineName) 180 ThunkReg = X86::EAX; 181 else if (MF.getName() == ECXRetpolineName) 182 ThunkReg = X86::ECX; 183 else if (MF.getName() == EDXRetpolineName) 184 ThunkReg = X86::EDX; 185 else if (MF.getName() == EDIRetpolineName) 186 ThunkReg = X86::EDI; 187 else 188 llvm_unreachable("Invalid thunk name on x86-32!"); 189 } 190 191 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 192 assert (MF.size() == 1); 193 MachineBasicBlock *Entry = &MF.front(); 194 Entry->clear(); 195 196 MachineBasicBlock *CaptureSpec = 197 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 198 MachineBasicBlock *CallTarget = 199 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 200 MCSymbol *TargetSym = MF.getContext().createTempSymbol(); 201 MF.push_back(CaptureSpec); 202 MF.push_back(CallTarget); 203 204 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32; 205 const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32; 206 207 Entry->addLiveIn(ThunkReg); 208 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym); 209 210 // The MIR verifier thinks that the CALL in the entry block will fall through 211 // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is 212 // the successor, but the MIR verifier doesn't know how to cope with that. 213 Entry->addSuccessor(CaptureSpec); 214 215 // In the capture loop for speculation, we want to stop the processor from 216 // speculating as fast as possible. On Intel processors, the PAUSE instruction 217 // will block speculation without consuming any execution resources. On AMD 218 // processors, the PAUSE instruction is (essentially) a nop, so we also use an 219 // LFENCE instruction which they have advised will stop speculation as well 220 // with minimal resource utilization. We still end the capture with a jump to 221 // form an infinite loop to fully guarantee that no matter what implementation 222 // of the x86 ISA, speculating this code path never escapes. 223 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE)); 224 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE)); 225 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec); 226 CaptureSpec->setMachineBlockAddressTaken(); 227 CaptureSpec->addSuccessor(CaptureSpec); 228 229 CallTarget->addLiveIn(ThunkReg); 230 CallTarget->setMachineBlockAddressTaken(); 231 CallTarget->setAlignment(Align(16)); 232 233 // Insert return address clobber 234 const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr; 235 const Register SPReg = Is64Bit ? X86::RSP : X86::ESP; 236 addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false, 237 0) 238 .addReg(ThunkReg); 239 240 CallTarget->back().setPreInstrSymbol(MF, TargetSym); 241 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc)); 242 } 243 244 FunctionPass *llvm::createX86IndirectThunksPass() { 245 return new X86IndirectThunks(); 246 } 247 248 char X86IndirectThunks::ID = 0; 249