10946e70aSDimitry Andric //==- X86IndirectThunks.cpp - Construct indirect call/jump thunks for x86 --=// 20946e70aSDimitry Andric // 30946e70aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40946e70aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50946e70aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60946e70aSDimitry Andric // 70946e70aSDimitry Andric //===----------------------------------------------------------------------===// 80946e70aSDimitry Andric /// \file 90946e70aSDimitry Andric /// 100946e70aSDimitry Andric /// Pass that injects an MI thunk that is used to lower indirect calls in a way 110946e70aSDimitry Andric /// that prevents speculation on some x86 processors and can be used to mitigate 120946e70aSDimitry Andric /// security vulnerabilities due to targeted speculative execution and side 130946e70aSDimitry Andric /// channels such as CVE-2017-5715. 140946e70aSDimitry Andric /// 150946e70aSDimitry Andric /// Currently supported thunks include: 160946e70aSDimitry Andric /// - Retpoline -- A RET-implemented trampoline that lowers indirect calls 170946e70aSDimitry Andric /// - LVI Thunk -- A CALL/JMP-implemented thunk that forces load serialization 180946e70aSDimitry Andric /// before making an indirect call/jump 190946e70aSDimitry Andric /// 200946e70aSDimitry Andric /// Note that the reason that this is implemented as a MachineFunctionPass and 210946e70aSDimitry Andric /// not a ModulePass is that ModulePasses at this point in the LLVM X86 pipeline 220946e70aSDimitry Andric /// serialize all transformations, which can consume lots of memory. 230946e70aSDimitry Andric /// 240946e70aSDimitry Andric /// TODO(chandlerc): All of this code could use better comments and 250946e70aSDimitry Andric /// documentation. 260946e70aSDimitry Andric /// 270946e70aSDimitry Andric //===----------------------------------------------------------------------===// 280946e70aSDimitry Andric 290946e70aSDimitry Andric #include "X86.h" 300946e70aSDimitry Andric #include "X86InstrBuilder.h" 310946e70aSDimitry Andric #include "X86Subtarget.h" 325ffd83dbSDimitry Andric #include "llvm/CodeGen/IndirectThunks.h" 330946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 3481ad6265SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 350946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 360946e70aSDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 370946e70aSDimitry Andric #include "llvm/CodeGen/Passes.h" 380946e70aSDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 390946e70aSDimitry Andric #include "llvm/IR/IRBuilder.h" 400946e70aSDimitry Andric #include "llvm/IR/Instructions.h" 410946e70aSDimitry Andric #include "llvm/IR/Module.h" 420946e70aSDimitry Andric #include "llvm/Support/CommandLine.h" 430946e70aSDimitry Andric #include "llvm/Support/Debug.h" 440946e70aSDimitry Andric #include "llvm/Support/raw_ostream.h" 455ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h" 460946e70aSDimitry Andric 470946e70aSDimitry Andric using namespace llvm; 480946e70aSDimitry Andric 490946e70aSDimitry Andric #define DEBUG_TYPE "x86-retpoline-thunks" 500946e70aSDimitry Andric 510946e70aSDimitry Andric static const char RetpolineNamePrefix[] = "__llvm_retpoline_"; 520946e70aSDimitry Andric static const char R11RetpolineName[] = "__llvm_retpoline_r11"; 530946e70aSDimitry Andric static const char EAXRetpolineName[] = "__llvm_retpoline_eax"; 540946e70aSDimitry Andric static const char ECXRetpolineName[] = "__llvm_retpoline_ecx"; 550946e70aSDimitry Andric static const char EDXRetpolineName[] = "__llvm_retpoline_edx"; 560946e70aSDimitry Andric static const char EDIRetpolineName[] = "__llvm_retpoline_edi"; 570946e70aSDimitry Andric 580946e70aSDimitry Andric static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_"; 590946e70aSDimitry Andric static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11"; 600946e70aSDimitry Andric 610946e70aSDimitry Andric namespace { 620946e70aSDimitry Andric struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> { 630946e70aSDimitry Andric const char *getThunkPrefix() { return RetpolineNamePrefix; } 64*0fca6ea1SDimitry Andric bool mayUseThunk(const MachineFunction &MF) { 650946e70aSDimitry Andric const auto &STI = MF.getSubtarget<X86Subtarget>(); 660946e70aSDimitry Andric return (STI.useRetpolineIndirectCalls() || 670946e70aSDimitry Andric STI.useRetpolineIndirectBranches()) && 680946e70aSDimitry Andric !STI.useRetpolineExternalThunk(); 690946e70aSDimitry Andric } 70*0fca6ea1SDimitry Andric bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, 71*0fca6ea1SDimitry Andric bool ExistingThunks); 720946e70aSDimitry Andric void populateThunk(MachineFunction &MF); 730946e70aSDimitry Andric }; 740946e70aSDimitry Andric 750946e70aSDimitry Andric struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> { 760946e70aSDimitry Andric const char *getThunkPrefix() { return LVIThunkNamePrefix; } 77*0fca6ea1SDimitry Andric bool mayUseThunk(const MachineFunction &MF) { 780946e70aSDimitry Andric return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity(); 790946e70aSDimitry Andric } 80*0fca6ea1SDimitry Andric bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, 81*0fca6ea1SDimitry Andric bool ExistingThunks) { 82*0fca6ea1SDimitry Andric if (ExistingThunks) 83*0fca6ea1SDimitry Andric return false; 840946e70aSDimitry Andric createThunkFunction(MMI, R11LVIThunkName); 85bdd1243dSDimitry Andric return true; 860946e70aSDimitry Andric } 870946e70aSDimitry Andric void populateThunk(MachineFunction &MF) { 885ffd83dbSDimitry Andric assert (MF.size() == 1); 890946e70aSDimitry Andric MachineBasicBlock *Entry = &MF.front(); 900946e70aSDimitry Andric Entry->clear(); 910946e70aSDimitry Andric 920946e70aSDimitry Andric // This code mitigates LVI by replacing each indirect call/jump with a 930946e70aSDimitry Andric // direct call/jump to a thunk that looks like: 940946e70aSDimitry Andric // ``` 950946e70aSDimitry Andric // lfence 960946e70aSDimitry Andric // jmpq *%r11 970946e70aSDimitry Andric // ``` 980946e70aSDimitry Andric // This ensures that if the value in register %r11 was loaded from memory, 990946e70aSDimitry Andric // then the value in %r11 is (architecturally) correct prior to the jump. 1000946e70aSDimitry Andric const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 1010946e70aSDimitry Andric BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE)); 1020946e70aSDimitry Andric BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11); 1030946e70aSDimitry Andric MF.front().addLiveIn(X86::R11); 1040946e70aSDimitry Andric } 1050946e70aSDimitry Andric }; 1060946e70aSDimitry Andric 107*0fca6ea1SDimitry Andric class X86IndirectThunks 108*0fca6ea1SDimitry Andric : public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> { 1090946e70aSDimitry Andric public: 1100946e70aSDimitry Andric static char ID; 1110946e70aSDimitry Andric 112*0fca6ea1SDimitry Andric X86IndirectThunks() : ThunkInserterPass(ID) {} 1130946e70aSDimitry Andric 1140946e70aSDimitry Andric StringRef getPassName() const override { return "X86 Indirect Thunks"; } 1150946e70aSDimitry Andric }; 1160946e70aSDimitry Andric 1170946e70aSDimitry Andric } // end anonymous namespace 1180946e70aSDimitry Andric 119bdd1243dSDimitry Andric bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI, 120*0fca6ea1SDimitry Andric MachineFunction &MF, 121*0fca6ea1SDimitry Andric bool ExistingThunks) { 122*0fca6ea1SDimitry Andric if (ExistingThunks) 123*0fca6ea1SDimitry Andric return false; 1240946e70aSDimitry Andric if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64) 1250946e70aSDimitry Andric createThunkFunction(MMI, R11RetpolineName); 1260946e70aSDimitry Andric else 1270946e70aSDimitry Andric for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName, 1280946e70aSDimitry Andric EDIRetpolineName}) 1290946e70aSDimitry Andric createThunkFunction(MMI, Name); 130bdd1243dSDimitry Andric return true; 1310946e70aSDimitry Andric } 1320946e70aSDimitry Andric 1330946e70aSDimitry Andric void RetpolineThunkInserter::populateThunk(MachineFunction &MF) { 1340946e70aSDimitry Andric bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; 1350946e70aSDimitry Andric Register ThunkReg; 1360946e70aSDimitry Andric if (Is64Bit) { 1370946e70aSDimitry Andric assert(MF.getName() == "__llvm_retpoline_r11" && 1380946e70aSDimitry Andric "Should only have an r11 thunk on 64-bit targets"); 1390946e70aSDimitry Andric 1400946e70aSDimitry Andric // __llvm_retpoline_r11: 1410946e70aSDimitry Andric // callq .Lr11_call_target 1420946e70aSDimitry Andric // .Lr11_capture_spec: 1430946e70aSDimitry Andric // pause 1440946e70aSDimitry Andric // lfence 1450946e70aSDimitry Andric // jmp .Lr11_capture_spec 1460946e70aSDimitry Andric // .align 16 1470946e70aSDimitry Andric // .Lr11_call_target: 1480946e70aSDimitry Andric // movq %r11, (%rsp) 1490946e70aSDimitry Andric // retq 1500946e70aSDimitry Andric ThunkReg = X86::R11; 1510946e70aSDimitry Andric } else { 1520946e70aSDimitry Andric // For 32-bit targets we need to emit a collection of thunks for various 1530946e70aSDimitry Andric // possible scratch registers as well as a fallback that uses EDI, which is 1540946e70aSDimitry Andric // normally callee saved. 1550946e70aSDimitry Andric // __llvm_retpoline_eax: 1560946e70aSDimitry Andric // calll .Leax_call_target 1570946e70aSDimitry Andric // .Leax_capture_spec: 1580946e70aSDimitry Andric // pause 1590946e70aSDimitry Andric // jmp .Leax_capture_spec 1600946e70aSDimitry Andric // .align 16 1610946e70aSDimitry Andric // .Leax_call_target: 1620946e70aSDimitry Andric // movl %eax, (%esp) # Clobber return addr 1630946e70aSDimitry Andric // retl 1640946e70aSDimitry Andric // 1650946e70aSDimitry Andric // __llvm_retpoline_ecx: 1660946e70aSDimitry Andric // ... # Same setup 1670946e70aSDimitry Andric // movl %ecx, (%esp) 1680946e70aSDimitry Andric // retl 1690946e70aSDimitry Andric // 1700946e70aSDimitry Andric // __llvm_retpoline_edx: 1710946e70aSDimitry Andric // ... # Same setup 1720946e70aSDimitry Andric // movl %edx, (%esp) 1730946e70aSDimitry Andric // retl 1740946e70aSDimitry Andric // 1750946e70aSDimitry Andric // __llvm_retpoline_edi: 1760946e70aSDimitry Andric // ... # Same setup 1770946e70aSDimitry Andric // movl %edi, (%esp) 1780946e70aSDimitry Andric // retl 1790946e70aSDimitry Andric if (MF.getName() == EAXRetpolineName) 1800946e70aSDimitry Andric ThunkReg = X86::EAX; 1810946e70aSDimitry Andric else if (MF.getName() == ECXRetpolineName) 1820946e70aSDimitry Andric ThunkReg = X86::ECX; 1830946e70aSDimitry Andric else if (MF.getName() == EDXRetpolineName) 1840946e70aSDimitry Andric ThunkReg = X86::EDX; 1850946e70aSDimitry Andric else if (MF.getName() == EDIRetpolineName) 1860946e70aSDimitry Andric ThunkReg = X86::EDI; 1870946e70aSDimitry Andric else 1880946e70aSDimitry Andric llvm_unreachable("Invalid thunk name on x86-32!"); 1890946e70aSDimitry Andric } 1900946e70aSDimitry Andric 1910946e70aSDimitry Andric const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo(); 1925ffd83dbSDimitry Andric assert (MF.size() == 1); 1930946e70aSDimitry Andric MachineBasicBlock *Entry = &MF.front(); 1940946e70aSDimitry Andric Entry->clear(); 1950946e70aSDimitry Andric 1960946e70aSDimitry Andric MachineBasicBlock *CaptureSpec = 1970946e70aSDimitry Andric MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 1980946e70aSDimitry Andric MachineBasicBlock *CallTarget = 1990946e70aSDimitry Andric MF.CreateMachineBasicBlock(Entry->getBasicBlock()); 2000946e70aSDimitry Andric MCSymbol *TargetSym = MF.getContext().createTempSymbol(); 2010946e70aSDimitry Andric MF.push_back(CaptureSpec); 2020946e70aSDimitry Andric MF.push_back(CallTarget); 2030946e70aSDimitry Andric 2040946e70aSDimitry Andric const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32; 205349cc55cSDimitry Andric const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32; 2060946e70aSDimitry Andric 2070946e70aSDimitry Andric Entry->addLiveIn(ThunkReg); 2080946e70aSDimitry Andric BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym); 2090946e70aSDimitry Andric 2100946e70aSDimitry Andric // The MIR verifier thinks that the CALL in the entry block will fall through 2110946e70aSDimitry Andric // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is 2120946e70aSDimitry Andric // the successor, but the MIR verifier doesn't know how to cope with that. 2130946e70aSDimitry Andric Entry->addSuccessor(CaptureSpec); 2140946e70aSDimitry Andric 2150946e70aSDimitry Andric // In the capture loop for speculation, we want to stop the processor from 2160946e70aSDimitry Andric // speculating as fast as possible. On Intel processors, the PAUSE instruction 2170946e70aSDimitry Andric // will block speculation without consuming any execution resources. On AMD 2180946e70aSDimitry Andric // processors, the PAUSE instruction is (essentially) a nop, so we also use an 2190946e70aSDimitry Andric // LFENCE instruction which they have advised will stop speculation as well 2200946e70aSDimitry Andric // with minimal resource utilization. We still end the capture with a jump to 2210946e70aSDimitry Andric // form an infinite loop to fully guarantee that no matter what implementation 2220946e70aSDimitry Andric // of the x86 ISA, speculating this code path never escapes. 2230946e70aSDimitry Andric BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE)); 2240946e70aSDimitry Andric BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE)); 2250946e70aSDimitry Andric BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec); 226bdd1243dSDimitry Andric CaptureSpec->setMachineBlockAddressTaken(); 2270946e70aSDimitry Andric CaptureSpec->addSuccessor(CaptureSpec); 2280946e70aSDimitry Andric 2290946e70aSDimitry Andric CallTarget->addLiveIn(ThunkReg); 230bdd1243dSDimitry Andric CallTarget->setMachineBlockAddressTaken(); 2310946e70aSDimitry Andric CallTarget->setAlignment(Align(16)); 2320946e70aSDimitry Andric 2330946e70aSDimitry Andric // Insert return address clobber 2340946e70aSDimitry Andric const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr; 2350946e70aSDimitry Andric const Register SPReg = Is64Bit ? X86::RSP : X86::ESP; 2360946e70aSDimitry Andric addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false, 2370946e70aSDimitry Andric 0) 2380946e70aSDimitry Andric .addReg(ThunkReg); 2390946e70aSDimitry Andric 2400946e70aSDimitry Andric CallTarget->back().setPreInstrSymbol(MF, TargetSym); 2410946e70aSDimitry Andric BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc)); 2420946e70aSDimitry Andric } 2430946e70aSDimitry Andric 2440946e70aSDimitry Andric FunctionPass *llvm::createX86IndirectThunksPass() { 2450946e70aSDimitry Andric return new X86IndirectThunks(); 2460946e70aSDimitry Andric } 2470946e70aSDimitry Andric 2480946e70aSDimitry Andric char X86IndirectThunks::ID = 0; 249