xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/X86/X86IndirectThunks.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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"
32*5ffd83dbSDimitry Andric #include "llvm/CodeGen/IndirectThunks.h"
330946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
340946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
350946e70aSDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
360946e70aSDimitry Andric #include "llvm/CodeGen/Passes.h"
370946e70aSDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
380946e70aSDimitry Andric #include "llvm/IR/IRBuilder.h"
390946e70aSDimitry Andric #include "llvm/IR/Instructions.h"
400946e70aSDimitry Andric #include "llvm/IR/Module.h"
410946e70aSDimitry Andric #include "llvm/Support/CommandLine.h"
420946e70aSDimitry Andric #include "llvm/Support/Debug.h"
430946e70aSDimitry Andric #include "llvm/Support/raw_ostream.h"
44*5ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h"
450946e70aSDimitry Andric 
460946e70aSDimitry Andric using namespace llvm;
470946e70aSDimitry Andric 
480946e70aSDimitry Andric #define DEBUG_TYPE "x86-retpoline-thunks"
490946e70aSDimitry Andric 
500946e70aSDimitry Andric static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
510946e70aSDimitry Andric static const char R11RetpolineName[] = "__llvm_retpoline_r11";
520946e70aSDimitry Andric static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
530946e70aSDimitry Andric static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
540946e70aSDimitry Andric static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
550946e70aSDimitry Andric static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
560946e70aSDimitry Andric 
570946e70aSDimitry Andric static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
580946e70aSDimitry Andric static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
590946e70aSDimitry Andric 
600946e70aSDimitry Andric namespace {
610946e70aSDimitry Andric struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {
620946e70aSDimitry Andric   const char *getThunkPrefix() { return RetpolineNamePrefix; }
630946e70aSDimitry Andric   bool mayUseThunk(const MachineFunction &MF) {
640946e70aSDimitry Andric     const auto &STI = MF.getSubtarget<X86Subtarget>();
650946e70aSDimitry Andric     return (STI.useRetpolineIndirectCalls() ||
660946e70aSDimitry Andric             STI.useRetpolineIndirectBranches()) &&
670946e70aSDimitry Andric            !STI.useRetpolineExternalThunk();
680946e70aSDimitry Andric   }
690946e70aSDimitry Andric   void insertThunks(MachineModuleInfo &MMI);
700946e70aSDimitry Andric   void populateThunk(MachineFunction &MF);
710946e70aSDimitry Andric };
720946e70aSDimitry Andric 
730946e70aSDimitry Andric struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
740946e70aSDimitry Andric   const char *getThunkPrefix() { return LVIThunkNamePrefix; }
750946e70aSDimitry Andric   bool mayUseThunk(const MachineFunction &MF) {
760946e70aSDimitry Andric     return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
770946e70aSDimitry Andric   }
780946e70aSDimitry Andric   void insertThunks(MachineModuleInfo &MMI) {
790946e70aSDimitry Andric     createThunkFunction(MMI, R11LVIThunkName);
800946e70aSDimitry Andric   }
810946e70aSDimitry Andric   void populateThunk(MachineFunction &MF) {
82*5ffd83dbSDimitry Andric     assert (MF.size() == 1);
830946e70aSDimitry Andric     MachineBasicBlock *Entry = &MF.front();
840946e70aSDimitry Andric     Entry->clear();
850946e70aSDimitry Andric 
860946e70aSDimitry Andric     // This code mitigates LVI by replacing each indirect call/jump with a
870946e70aSDimitry Andric     // direct call/jump to a thunk that looks like:
880946e70aSDimitry Andric     // ```
890946e70aSDimitry Andric     // lfence
900946e70aSDimitry Andric     // jmpq *%r11
910946e70aSDimitry Andric     // ```
920946e70aSDimitry Andric     // This ensures that if the value in register %r11 was loaded from memory,
930946e70aSDimitry Andric     // then the value in %r11 is (architecturally) correct prior to the jump.
940946e70aSDimitry Andric     const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
950946e70aSDimitry Andric     BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE));
960946e70aSDimitry Andric     BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11);
970946e70aSDimitry Andric     MF.front().addLiveIn(X86::R11);
980946e70aSDimitry Andric     return;
990946e70aSDimitry Andric   }
1000946e70aSDimitry Andric };
1010946e70aSDimitry Andric 
1020946e70aSDimitry Andric class X86IndirectThunks : public MachineFunctionPass {
1030946e70aSDimitry Andric public:
1040946e70aSDimitry Andric   static char ID;
1050946e70aSDimitry Andric 
1060946e70aSDimitry Andric   X86IndirectThunks() : MachineFunctionPass(ID) {}
1070946e70aSDimitry Andric 
1080946e70aSDimitry Andric   StringRef getPassName() const override { return "X86 Indirect Thunks"; }
1090946e70aSDimitry Andric 
1100946e70aSDimitry Andric   bool doInitialization(Module &M) override;
1110946e70aSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
1120946e70aSDimitry Andric 
1130946e70aSDimitry Andric private:
1140946e70aSDimitry Andric   std::tuple<RetpolineThunkInserter, LVIThunkInserter> TIs;
1150946e70aSDimitry Andric 
1160946e70aSDimitry Andric   // FIXME: When LLVM moves to C++17, these can become folds
1170946e70aSDimitry Andric   template <typename... ThunkInserterT>
1180946e70aSDimitry Andric   static void initTIs(Module &M,
1190946e70aSDimitry Andric                       std::tuple<ThunkInserterT...> &ThunkInserters) {
1200946e70aSDimitry Andric     (void)std::initializer_list<int>{
1210946e70aSDimitry Andric         (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
1220946e70aSDimitry Andric   }
1230946e70aSDimitry Andric   template <typename... ThunkInserterT>
1240946e70aSDimitry Andric   static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
1250946e70aSDimitry Andric                      std::tuple<ThunkInserterT...> &ThunkInserters) {
1260946e70aSDimitry Andric     bool Modified = false;
1270946e70aSDimitry Andric     (void)std::initializer_list<int>{
1280946e70aSDimitry Andric         Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
1290946e70aSDimitry Andric     return Modified;
1300946e70aSDimitry Andric   }
1310946e70aSDimitry Andric };
1320946e70aSDimitry Andric 
1330946e70aSDimitry Andric } // end anonymous namespace
1340946e70aSDimitry Andric 
1350946e70aSDimitry Andric void RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI) {
1360946e70aSDimitry Andric   if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)
1370946e70aSDimitry Andric     createThunkFunction(MMI, R11RetpolineName);
1380946e70aSDimitry Andric   else
1390946e70aSDimitry Andric     for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
1400946e70aSDimitry Andric                            EDIRetpolineName})
1410946e70aSDimitry Andric       createThunkFunction(MMI, Name);
1420946e70aSDimitry Andric }
1430946e70aSDimitry Andric 
1440946e70aSDimitry Andric void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {
1450946e70aSDimitry Andric   bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;
1460946e70aSDimitry Andric   Register ThunkReg;
1470946e70aSDimitry Andric   if (Is64Bit) {
1480946e70aSDimitry Andric     assert(MF.getName() == "__llvm_retpoline_r11" &&
1490946e70aSDimitry Andric            "Should only have an r11 thunk on 64-bit targets");
1500946e70aSDimitry Andric 
1510946e70aSDimitry Andric     // __llvm_retpoline_r11:
1520946e70aSDimitry Andric     //   callq .Lr11_call_target
1530946e70aSDimitry Andric     // .Lr11_capture_spec:
1540946e70aSDimitry Andric     //   pause
1550946e70aSDimitry Andric     //   lfence
1560946e70aSDimitry Andric     //   jmp .Lr11_capture_spec
1570946e70aSDimitry Andric     // .align 16
1580946e70aSDimitry Andric     // .Lr11_call_target:
1590946e70aSDimitry Andric     //   movq %r11, (%rsp)
1600946e70aSDimitry Andric     //   retq
1610946e70aSDimitry Andric     ThunkReg = X86::R11;
1620946e70aSDimitry Andric   } else {
1630946e70aSDimitry Andric     // For 32-bit targets we need to emit a collection of thunks for various
1640946e70aSDimitry Andric     // possible scratch registers as well as a fallback that uses EDI, which is
1650946e70aSDimitry Andric     // normally callee saved.
1660946e70aSDimitry Andric     //   __llvm_retpoline_eax:
1670946e70aSDimitry Andric     //         calll .Leax_call_target
1680946e70aSDimitry Andric     //   .Leax_capture_spec:
1690946e70aSDimitry Andric     //         pause
1700946e70aSDimitry Andric     //         jmp .Leax_capture_spec
1710946e70aSDimitry Andric     //   .align 16
1720946e70aSDimitry Andric     //   .Leax_call_target:
1730946e70aSDimitry Andric     //         movl %eax, (%esp)  # Clobber return addr
1740946e70aSDimitry Andric     //         retl
1750946e70aSDimitry Andric     //
1760946e70aSDimitry Andric     //   __llvm_retpoline_ecx:
1770946e70aSDimitry Andric     //   ... # Same setup
1780946e70aSDimitry Andric     //         movl %ecx, (%esp)
1790946e70aSDimitry Andric     //         retl
1800946e70aSDimitry Andric     //
1810946e70aSDimitry Andric     //   __llvm_retpoline_edx:
1820946e70aSDimitry Andric     //   ... # Same setup
1830946e70aSDimitry Andric     //         movl %edx, (%esp)
1840946e70aSDimitry Andric     //         retl
1850946e70aSDimitry Andric     //
1860946e70aSDimitry Andric     //   __llvm_retpoline_edi:
1870946e70aSDimitry Andric     //   ... # Same setup
1880946e70aSDimitry Andric     //         movl %edi, (%esp)
1890946e70aSDimitry Andric     //         retl
1900946e70aSDimitry Andric     if (MF.getName() == EAXRetpolineName)
1910946e70aSDimitry Andric       ThunkReg = X86::EAX;
1920946e70aSDimitry Andric     else if (MF.getName() == ECXRetpolineName)
1930946e70aSDimitry Andric       ThunkReg = X86::ECX;
1940946e70aSDimitry Andric     else if (MF.getName() == EDXRetpolineName)
1950946e70aSDimitry Andric       ThunkReg = X86::EDX;
1960946e70aSDimitry Andric     else if (MF.getName() == EDIRetpolineName)
1970946e70aSDimitry Andric       ThunkReg = X86::EDI;
1980946e70aSDimitry Andric     else
1990946e70aSDimitry Andric       llvm_unreachable("Invalid thunk name on x86-32!");
2000946e70aSDimitry Andric   }
2010946e70aSDimitry Andric 
2020946e70aSDimitry Andric   const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
203*5ffd83dbSDimitry Andric   assert (MF.size() == 1);
2040946e70aSDimitry Andric   MachineBasicBlock *Entry = &MF.front();
2050946e70aSDimitry Andric   Entry->clear();
2060946e70aSDimitry Andric 
2070946e70aSDimitry Andric   MachineBasicBlock *CaptureSpec =
2080946e70aSDimitry Andric       MF.CreateMachineBasicBlock(Entry->getBasicBlock());
2090946e70aSDimitry Andric   MachineBasicBlock *CallTarget =
2100946e70aSDimitry Andric       MF.CreateMachineBasicBlock(Entry->getBasicBlock());
2110946e70aSDimitry Andric   MCSymbol *TargetSym = MF.getContext().createTempSymbol();
2120946e70aSDimitry Andric   MF.push_back(CaptureSpec);
2130946e70aSDimitry Andric   MF.push_back(CallTarget);
2140946e70aSDimitry Andric 
2150946e70aSDimitry Andric   const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
2160946e70aSDimitry Andric   const unsigned RetOpc = Is64Bit ? X86::RETQ : X86::RETL;
2170946e70aSDimitry Andric 
2180946e70aSDimitry Andric   Entry->addLiveIn(ThunkReg);
2190946e70aSDimitry Andric   BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
2200946e70aSDimitry Andric 
2210946e70aSDimitry Andric   // The MIR verifier thinks that the CALL in the entry block will fall through
2220946e70aSDimitry Andric   // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is
2230946e70aSDimitry Andric   // the successor, but the MIR verifier doesn't know how to cope with that.
2240946e70aSDimitry Andric   Entry->addSuccessor(CaptureSpec);
2250946e70aSDimitry Andric 
2260946e70aSDimitry Andric   // In the capture loop for speculation, we want to stop the processor from
2270946e70aSDimitry Andric   // speculating as fast as possible. On Intel processors, the PAUSE instruction
2280946e70aSDimitry Andric   // will block speculation without consuming any execution resources. On AMD
2290946e70aSDimitry Andric   // processors, the PAUSE instruction is (essentially) a nop, so we also use an
2300946e70aSDimitry Andric   // LFENCE instruction which they have advised will stop speculation as well
2310946e70aSDimitry Andric   // with minimal resource utilization. We still end the capture with a jump to
2320946e70aSDimitry Andric   // form an infinite loop to fully guarantee that no matter what implementation
2330946e70aSDimitry Andric   // of the x86 ISA, speculating this code path never escapes.
2340946e70aSDimitry Andric   BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE));
2350946e70aSDimitry Andric   BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE));
2360946e70aSDimitry Andric   BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec);
2370946e70aSDimitry Andric   CaptureSpec->setHasAddressTaken();
2380946e70aSDimitry Andric   CaptureSpec->addSuccessor(CaptureSpec);
2390946e70aSDimitry Andric 
2400946e70aSDimitry Andric   CallTarget->addLiveIn(ThunkReg);
2410946e70aSDimitry Andric   CallTarget->setHasAddressTaken();
2420946e70aSDimitry Andric   CallTarget->setAlignment(Align(16));
2430946e70aSDimitry Andric 
2440946e70aSDimitry Andric   // Insert return address clobber
2450946e70aSDimitry Andric   const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
2460946e70aSDimitry Andric   const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
2470946e70aSDimitry Andric   addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,
2480946e70aSDimitry Andric                0)
2490946e70aSDimitry Andric       .addReg(ThunkReg);
2500946e70aSDimitry Andric 
2510946e70aSDimitry Andric   CallTarget->back().setPreInstrSymbol(MF, TargetSym);
2520946e70aSDimitry Andric   BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
2530946e70aSDimitry Andric }
2540946e70aSDimitry Andric 
2550946e70aSDimitry Andric FunctionPass *llvm::createX86IndirectThunksPass() {
2560946e70aSDimitry Andric   return new X86IndirectThunks();
2570946e70aSDimitry Andric }
2580946e70aSDimitry Andric 
2590946e70aSDimitry Andric char X86IndirectThunks::ID = 0;
2600946e70aSDimitry Andric 
2610946e70aSDimitry Andric bool X86IndirectThunks::doInitialization(Module &M) {
2620946e70aSDimitry Andric   initTIs(M, TIs);
2630946e70aSDimitry Andric   return false;
2640946e70aSDimitry Andric }
2650946e70aSDimitry Andric 
2660946e70aSDimitry Andric bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
2670946e70aSDimitry Andric   LLVM_DEBUG(dbgs() << getPassName() << '\n');
2680946e70aSDimitry Andric   auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
2690946e70aSDimitry Andric   return runTIs(MMI, MF, TIs);
2700946e70aSDimitry Andric }
271