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/MachineFunction.h" 33 #include "llvm/CodeGen/MachineInstrBuilder.h" 34 #include "llvm/CodeGen/MachineModuleInfo.h" 35 #include "llvm/CodeGen/Passes.h" 36 #include "llvm/CodeGen/TargetPassConfig.h" 37 #include "llvm/IR/IRBuilder.h" 38 #include "llvm/IR/Instructions.h" 39 #include "llvm/IR/Module.h" 40 #include "llvm/Support/CommandLine.h" 41 #include "llvm/Support/Debug.h" 42 #include "llvm/Support/raw_ostream.h" 43 #include "llvm/Target/TargetMachine.h" 44 45 using namespace llvm; 46 47 #define DEBUG_TYPE "x86-retpoline-thunks" 48 49 static const char RetpolineNamePrefix[] = "__llvm_retpoline_"; 50 static const char R11RetpolineName[] = "__llvm_retpoline_r11"; 51 static const char EAXRetpolineName[] = "__llvm_retpoline_eax"; 52 static const char ECXRetpolineName[] = "__llvm_retpoline_ecx"; 53 static const char EDXRetpolineName[] = "__llvm_retpoline_edx"; 54 static const char EDIRetpolineName[] = "__llvm_retpoline_edi"; 55 56 static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_"; 57 static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11"; 58 59 namespace { 60 template <typename Derived> class ThunkInserter { 61 Derived &getDerived() { return *static_cast<Derived *>(this); } 62 63 protected: 64 bool InsertedThunks; 65 void doInitialization(Module &M) {} 66 void createThunkFunction(MachineModuleInfo &MMI, StringRef Name); 67 68 public: 69 void init(Module &M) { 70 InsertedThunks = false; 71 getDerived().doInitialization(M); 72 } 73 // return `true` if `MMI` or `MF` was modified 74 bool run(MachineModuleInfo &MMI, MachineFunction &MF); 75 }; 76 77 struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> { 78 const char *getThunkPrefix() { return RetpolineNamePrefix; } 79 bool mayUseThunk(const MachineFunction &MF) { 80 const auto &STI = MF.getSubtarget<X86Subtarget>(); 81 return (STI.useRetpolineIndirectCalls() || 82 STI.useRetpolineIndirectBranches()) && 83 !STI.useRetpolineExternalThunk(); 84 } 85 void insertThunks(MachineModuleInfo &MMI); 86 void populateThunk(MachineFunction &MF); 87 }; 88 89 struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> { 90 const char *getThunkPrefix() { return LVIThunkNamePrefix; } 91 bool mayUseThunk(const MachineFunction &MF) { 92 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity(); 93 } 94 void insertThunks(MachineModuleInfo &MMI) { 95 createThunkFunction(MMI, R11LVIThunkName); 96 } 97 void populateThunk(MachineFunction &MF) { 98 // Grab the entry MBB and erase any other blocks. O0 codegen appears to 99 // generate two bbs for the entry block. 100 MachineBasicBlock *Entry = &MF.front(); 101 Entry->clear(); 102 while (MF.size() > 1) 103 MF.erase(std::next(MF.begin())); 104 105 // This code mitigates LVI by replacing each indirect call/jump with a 106 // direct call/jump to a thunk that looks like: 107 // ``` 108 // lfence 109 // jmpq *%r11 110 // ``` 111 // This ensures that if the value in register %r11 was loaded from memory, 112 // then the value in %r11 is (architecturally) correct prior to the jump. 113 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 114 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE)); 115 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11); 116 MF.front().addLiveIn(X86::R11); 117 return; 118 } 119 }; 120 121 class X86IndirectThunks : public MachineFunctionPass { 122 public: 123 static char ID; 124 125 X86IndirectThunks() : MachineFunctionPass(ID) {} 126 127 StringRef getPassName() const override { return "X86 Indirect Thunks"; } 128 129 bool doInitialization(Module &M) override; 130 bool runOnMachineFunction(MachineFunction &MF) override; 131 132 void getAnalysisUsage(AnalysisUsage &AU) const override { 133 MachineFunctionPass::getAnalysisUsage(AU); 134 AU.addRequired<MachineModuleInfoWrapperPass>(); 135 AU.addPreserved<MachineModuleInfoWrapperPass>(); 136 } 137 138 private: 139 std::tuple<RetpolineThunkInserter, LVIThunkInserter> TIs; 140 141 // FIXME: When LLVM moves to C++17, these can become folds 142 template <typename... ThunkInserterT> 143 static void initTIs(Module &M, 144 std::tuple<ThunkInserterT...> &ThunkInserters) { 145 (void)std::initializer_list<int>{ 146 (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...}; 147 } 148 template <typename... ThunkInserterT> 149 static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF, 150 std::tuple<ThunkInserterT...> &ThunkInserters) { 151 bool Modified = false; 152 (void)std::initializer_list<int>{ 153 Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...}; 154 return Modified; 155 } 156 }; 157 158 } // end anonymous namespace 159 160 void RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI) { 161 if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64) 162 createThunkFunction(MMI, R11RetpolineName); 163 else 164 for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName, 165 EDIRetpolineName}) 166 createThunkFunction(MMI, Name); 167 } 168 169 void RetpolineThunkInserter::populateThunk(MachineFunction &MF) { 170 bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; 171 Register ThunkReg; 172 if (Is64Bit) { 173 assert(MF.getName() == "__llvm_retpoline_r11" && 174 "Should only have an r11 thunk on 64-bit targets"); 175 176 // __llvm_retpoline_r11: 177 // callq .Lr11_call_target 178 // .Lr11_capture_spec: 179 // pause 180 // lfence 181 // jmp .Lr11_capture_spec 182 // .align 16 183 // .Lr11_call_target: 184 // movq %r11, (%rsp) 185 // retq 186 ThunkReg = X86::R11; 187 } else { 188 // For 32-bit targets we need to emit a collection of thunks for various 189 // possible scratch registers as well as a fallback that uses EDI, which is 190 // normally callee saved. 191 // __llvm_retpoline_eax: 192 // calll .Leax_call_target 193 // .Leax_capture_spec: 194 // pause 195 // jmp .Leax_capture_spec 196 // .align 16 197 // .Leax_call_target: 198 // movl %eax, (%esp) # Clobber return addr 199 // retl 200 // 201 // __llvm_retpoline_ecx: 202 // ... # Same setup 203 // movl %ecx, (%esp) 204 // retl 205 // 206 // __llvm_retpoline_edx: 207 // ... # Same setup 208 // movl %edx, (%esp) 209 // retl 210 // 211 // __llvm_retpoline_edi: 212 // ... # Same setup 213 // movl %edi, (%esp) 214 // retl 215 if (MF.getName() == EAXRetpolineName) 216 ThunkReg = X86::EAX; 217 else if (MF.getName() == ECXRetpolineName) 218 ThunkReg = X86::ECX; 219 else if (MF.getName() == EDXRetpolineName) 220 ThunkReg = X86::EDX; 221 else if (MF.getName() == EDIRetpolineName) 222 ThunkReg = X86::EDI; 223 else 224 llvm_unreachable("Invalid thunk name on x86-32!"); 225 } 226 227 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 228 // Grab the entry MBB and erase any other blocks. O0 codegen appears to 229 // generate two bbs for the entry block. 230 MachineBasicBlock *Entry = &MF.front(); 231 Entry->clear(); 232 while (MF.size() > 1) 233 MF.erase(std::next(MF.begin())); 234 235 MachineBasicBlock *CaptureSpec = 236 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 237 MachineBasicBlock *CallTarget = 238 MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 239 MCSymbol *TargetSym = MF.getContext().createTempSymbol(); 240 MF.push_back(CaptureSpec); 241 MF.push_back(CallTarget); 242 243 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32; 244 const unsigned RetOpc = Is64Bit ? X86::RETQ : X86::RETL; 245 246 Entry->addLiveIn(ThunkReg); 247 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym); 248 249 // The MIR verifier thinks that the CALL in the entry block will fall through 250 // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is 251 // the successor, but the MIR verifier doesn't know how to cope with that. 252 Entry->addSuccessor(CaptureSpec); 253 254 // In the capture loop for speculation, we want to stop the processor from 255 // speculating as fast as possible. On Intel processors, the PAUSE instruction 256 // will block speculation without consuming any execution resources. On AMD 257 // processors, the PAUSE instruction is (essentially) a nop, so we also use an 258 // LFENCE instruction which they have advised will stop speculation as well 259 // with minimal resource utilization. We still end the capture with a jump to 260 // form an infinite loop to fully guarantee that no matter what implementation 261 // of the x86 ISA, speculating this code path never escapes. 262 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE)); 263 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE)); 264 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec); 265 CaptureSpec->setHasAddressTaken(); 266 CaptureSpec->addSuccessor(CaptureSpec); 267 268 CallTarget->addLiveIn(ThunkReg); 269 CallTarget->setHasAddressTaken(); 270 CallTarget->setAlignment(Align(16)); 271 272 // Insert return address clobber 273 const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr; 274 const Register SPReg = Is64Bit ? X86::RSP : X86::ESP; 275 addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false, 276 0) 277 .addReg(ThunkReg); 278 279 CallTarget->back().setPreInstrSymbol(MF, TargetSym); 280 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc)); 281 } 282 283 template <typename Derived> 284 void ThunkInserter<Derived>::createThunkFunction(MachineModuleInfo &MMI, 285 StringRef Name) { 286 assert(Name.startswith(getDerived().getThunkPrefix()) && 287 "Created a thunk with an unexpected prefix!"); 288 289 Module &M = const_cast<Module &>(*MMI.getModule()); 290 LLVMContext &Ctx = M.getContext(); 291 auto Type = FunctionType::get(Type::getVoidTy(Ctx), false); 292 Function *F = 293 Function::Create(Type, GlobalValue::LinkOnceODRLinkage, Name, &M); 294 F->setVisibility(GlobalValue::HiddenVisibility); 295 F->setComdat(M.getOrInsertComdat(Name)); 296 297 // Add Attributes so that we don't create a frame, unwind information, or 298 // inline. 299 AttrBuilder B; 300 B.addAttribute(llvm::Attribute::NoUnwind); 301 B.addAttribute(llvm::Attribute::Naked); 302 F->addAttributes(llvm::AttributeList::FunctionIndex, B); 303 304 // Populate our function a bit so that we can verify. 305 BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F); 306 IRBuilder<> Builder(Entry); 307 308 Builder.CreateRetVoid(); 309 310 // MachineFunctions/MachineBasicBlocks aren't created automatically for the 311 // IR-level constructs we already made. Create them and insert them into the 312 // module. 313 MachineFunction &MF = MMI.getOrCreateMachineFunction(*F); 314 MachineBasicBlock *EntryMBB = MF.CreateMachineBasicBlock(Entry); 315 316 // Insert EntryMBB into MF. It's not in the module until we do this. 317 MF.insert(MF.end(), EntryMBB); 318 // Set MF properties. We never use vregs... 319 MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs); 320 } 321 322 template <typename Derived> 323 bool ThunkInserter<Derived>::run(MachineModuleInfo &MMI, MachineFunction &MF) { 324 // If MF is not a thunk, check to see if we need to insert a thunk. 325 if (!MF.getName().startswith(getDerived().getThunkPrefix())) { 326 // If we've already inserted a thunk, nothing else to do. 327 if (InsertedThunks) 328 return false; 329 330 // Only add a thunk if one of the functions has the corresponding feature 331 // enabled in its subtarget, and doesn't enable external thunks. 332 // FIXME: Conditionalize on indirect calls so we don't emit a thunk when 333 // nothing will end up calling it. 334 // FIXME: It's a little silly to look at every function just to enumerate 335 // the subtargets, but eventually we'll want to look at them for indirect 336 // calls, so maybe this is OK. 337 if (!getDerived().mayUseThunk(MF)) 338 return false; 339 340 getDerived().insertThunks(MMI); 341 InsertedThunks = true; 342 return true; 343 } 344 345 // If this *is* a thunk function, we need to populate it with the correct MI. 346 getDerived().populateThunk(MF); 347 return true; 348 } 349 350 FunctionPass *llvm::createX86IndirectThunksPass() { 351 return new X86IndirectThunks(); 352 } 353 354 char X86IndirectThunks::ID = 0; 355 356 bool X86IndirectThunks::doInitialization(Module &M) { 357 initTIs(M, TIs); 358 return false; 359 } 360 361 bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) { 362 LLVM_DEBUG(dbgs() << getPassName() << '\n'); 363 auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); 364 return runTIs(MMI, MF, TIs); 365 } 366