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/MachineInstrBuilder.h" 35 #include "llvm/CodeGen/MachineModuleInfo.h" 36 #include "llvm/CodeGen/Passes.h" 37 #include "llvm/CodeGen/TargetPassConfig.h" 38 #include "llvm/IR/IRBuilder.h" 39 #include "llvm/IR/Instructions.h" 40 #include "llvm/IR/Module.h" 41 #include "llvm/Support/CommandLine.h" 42 #include "llvm/Support/Debug.h" 43 #include "llvm/Support/raw_ostream.h" 44 #include "llvm/Target/TargetMachine.h" 45 46 using namespace llvm; 47 48 #define DEBUG_TYPE "x86-retpoline-thunks" 49 50 static const char RetpolineNamePrefix[] = "__llvm_retpoline_"; 51 static const char R11RetpolineName[] = "__llvm_retpoline_r11"; 52 static const char EAXRetpolineName[] = "__llvm_retpoline_eax"; 53 static const char ECXRetpolineName[] = "__llvm_retpoline_ecx"; 54 static const char EDXRetpolineName[] = "__llvm_retpoline_edx"; 55 static const char EDIRetpolineName[] = "__llvm_retpoline_edi"; 56 57 static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_"; 58 static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11"; 59 60 namespace { 61 struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> { 62 const char *getThunkPrefix() { return RetpolineNamePrefix; } 63 bool mayUseThunk(const MachineFunction &MF) { 64 const auto &STI = MF.getSubtarget<X86Subtarget>(); 65 return (STI.useRetpolineIndirectCalls() || 66 STI.useRetpolineIndirectBranches()) && 67 !STI.useRetpolineExternalThunk(); 68 } 69 void insertThunks(MachineModuleInfo &MMI); 70 void populateThunk(MachineFunction &MF); 71 }; 72 73 struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> { 74 const char *getThunkPrefix() { return LVIThunkNamePrefix; } 75 bool mayUseThunk(const MachineFunction &MF) { 76 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity(); 77 } 78 void insertThunks(MachineModuleInfo &MMI) { 79 createThunkFunction(MMI, R11LVIThunkName); 80 } 81 void populateThunk(MachineFunction &MF) { 82 // Grab the entry MBB and erase any other blocks. O0 codegen appears to 83 // generate two bbs for the entry block. 84 MachineBasicBlock *Entry = &MF.front(); 85 Entry->clear(); 86 while (MF.size() > 1) 87 MF.erase(std::next(MF.begin())); 88 89 // This code mitigates LVI by replacing each indirect call/jump with a 90 // direct call/jump to a thunk that looks like: 91 // ``` 92 // lfence 93 // jmpq *%r11 94 // ``` 95 // This ensures that if the value in register %r11 was loaded from memory, 96 // then the value in %r11 is (architecturally) correct prior to the jump. 97 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 98 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE)); 99 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11); 100 MF.front().addLiveIn(X86::R11); 101 return; 102 } 103 }; 104 105 class X86IndirectThunks : public MachineFunctionPass { 106 public: 107 static char ID; 108 109 X86IndirectThunks() : MachineFunctionPass(ID) {} 110 111 StringRef getPassName() const override { return "X86 Indirect Thunks"; } 112 113 bool doInitialization(Module &M) override; 114 bool runOnMachineFunction(MachineFunction &MF) override; 115 116 void getAnalysisUsage(AnalysisUsage &AU) const override { 117 MachineFunctionPass::getAnalysisUsage(AU); 118 AU.addRequired<MachineModuleInfoWrapperPass>(); 119 AU.addPreserved<MachineModuleInfoWrapperPass>(); 120 } 121 122 private: 123 std::tuple<RetpolineThunkInserter, LVIThunkInserter> TIs; 124 125 // FIXME: When LLVM moves to C++17, these can become folds 126 template <typename... ThunkInserterT> 127 static void initTIs(Module &M, 128 std::tuple<ThunkInserterT...> &ThunkInserters) { 129 (void)std::initializer_list<int>{ 130 (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...}; 131 } 132 template <typename... ThunkInserterT> 133 static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF, 134 std::tuple<ThunkInserterT...> &ThunkInserters) { 135 bool Modified = false; 136 (void)std::initializer_list<int>{ 137 Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...}; 138 return Modified; 139 } 140 }; 141 142 } // end anonymous namespace 143 144 void RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI) { 145 if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64) 146 createThunkFunction(MMI, R11RetpolineName); 147 else 148 for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName, 149 EDIRetpolineName}) 150 createThunkFunction(MMI, Name); 151 } 152 153 void RetpolineThunkInserter::populateThunk(MachineFunction &MF) { 154 bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; 155 Register ThunkReg; 156 if (Is64Bit) { 157 assert(MF.getName() == "__llvm_retpoline_r11" && 158 "Should only have an r11 thunk on 64-bit targets"); 159 160 // __llvm_retpoline_r11: 161 // callq .Lr11_call_target 162 // .Lr11_capture_spec: 163 // pause 164 // lfence 165 // jmp .Lr11_capture_spec 166 // .align 16 167 // .Lr11_call_target: 168 // movq %r11, (%rsp) 169 // retq 170 ThunkReg = X86::R11; 171 } else { 172 // For 32-bit targets we need to emit a collection of thunks for various 173 // possible scratch registers as well as a fallback that uses EDI, which is 174 // normally callee saved. 175 // __llvm_retpoline_eax: 176 // calll .Leax_call_target 177 // .Leax_capture_spec: 178 // pause 179 // jmp .Leax_capture_spec 180 // .align 16 181 // .Leax_call_target: 182 // movl %eax, (%esp) # Clobber return addr 183 // retl 184 // 185 // __llvm_retpoline_ecx: 186 // ... # Same setup 187 // movl %ecx, (%esp) 188 // retl 189 // 190 // __llvm_retpoline_edx: 191 // ... # Same setup 192 // movl %edx, (%esp) 193 // retl 194 // 195 // __llvm_retpoline_edi: 196 // ... # Same setup 197 // movl %edi, (%esp) 198 // retl 199 if (MF.getName() == EAXRetpolineName) 200 ThunkReg = X86::EAX; 201 else if (MF.getName() == ECXRetpolineName) 202 ThunkReg = X86::ECX; 203 else if (MF.getName() == EDXRetpolineName) 204 ThunkReg = X86::EDX; 205 else if (MF.getName() == EDIRetpolineName) 206 ThunkReg = X86::EDI; 207 else 208 llvm_unreachable("Invalid thunk name on x86-32!"); 209 } 210 211 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 212 // Grab the entry MBB and erase any other blocks. O0 codegen appears to 213 // generate two bbs for the entry block. 214 MachineBasicBlock *Entry = &MF.front(); 215 Entry->clear(); 216 while (MF.size() > 1) 217 MF.erase(std::next(MF.begin())); 218 219 MachineBasicBlock *CaptureSpec = 220 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 221 MachineBasicBlock *CallTarget = 222 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 223 MCSymbol *TargetSym = MF.getContext().createTempSymbol(); 224 MF.push_back(CaptureSpec); 225 MF.push_back(CallTarget); 226 227 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32; 228 const unsigned RetOpc = Is64Bit ? X86::RETQ : X86::RETL; 229 230 Entry->addLiveIn(ThunkReg); 231 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym); 232 233 // The MIR verifier thinks that the CALL in the entry block will fall through 234 // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is 235 // the successor, but the MIR verifier doesn't know how to cope with that. 236 Entry->addSuccessor(CaptureSpec); 237 238 // In the capture loop for speculation, we want to stop the processor from 239 // speculating as fast as possible. On Intel processors, the PAUSE instruction 240 // will block speculation without consuming any execution resources. On AMD 241 // processors, the PAUSE instruction is (essentially) a nop, so we also use an 242 // LFENCE instruction which they have advised will stop speculation as well 243 // with minimal resource utilization. We still end the capture with a jump to 244 // form an infinite loop to fully guarantee that no matter what implementation 245 // of the x86 ISA, speculating this code path never escapes. 246 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE)); 247 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE)); 248 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec); 249 CaptureSpec->setHasAddressTaken(); 250 CaptureSpec->addSuccessor(CaptureSpec); 251 252 CallTarget->addLiveIn(ThunkReg); 253 CallTarget->setHasAddressTaken(); 254 CallTarget->setAlignment(Align(16)); 255 256 // Insert return address clobber 257 const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr; 258 const Register SPReg = Is64Bit ? X86::RSP : X86::ESP; 259 addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false, 260 0) 261 .addReg(ThunkReg); 262 263 CallTarget->back().setPreInstrSymbol(MF, TargetSym); 264 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc)); 265 } 266 267 FunctionPass *llvm::createX86IndirectThunksPass() { 268 return new X86IndirectThunks(); 269 } 270 271 char X86IndirectThunks::ID = 0; 272 273 bool X86IndirectThunks::doInitialization(Module &M) { 274 initTIs(M, TIs); 275 return false; 276 } 277 278 bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) { 279 LLVM_DEBUG(dbgs() << getPassName() << '\n'); 280 auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); 281 return runTIs(MMI, MF, TIs); 282 } 283