xref: /freebsd/contrib/llvm-project/llvm/lib/Target/X86/X86IndirectThunks.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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> {
getThunkPrefix__anon40fda1900111::RetpolineThunkInserter630946e70aSDimitry Andric   const char *getThunkPrefix() { return RetpolineNamePrefix; }
mayUseThunk__anon40fda1900111::RetpolineThunkInserter64*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> {
getThunkPrefix__anon40fda1900111::LVIThunkInserter760946e70aSDimitry Andric   const char *getThunkPrefix() { return LVIThunkNamePrefix; }
mayUseThunk__anon40fda1900111::LVIThunkInserter77*0fca6ea1SDimitry Andric   bool mayUseThunk(const MachineFunction &MF) {
780946e70aSDimitry Andric     return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
790946e70aSDimitry Andric   }
insertThunks__anon40fda1900111::LVIThunkInserter80*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   }
populateThunk__anon40fda1900111::LVIThunkInserter870946e70aSDimitry 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 
X86IndirectThunks()112*0fca6ea1SDimitry Andric   X86IndirectThunks() : ThunkInserterPass(ID) {}
1130946e70aSDimitry Andric 
getPassName() const1140946e70aSDimitry Andric   StringRef getPassName() const override { return "X86 Indirect Thunks"; }
1150946e70aSDimitry Andric };
1160946e70aSDimitry Andric 
1170946e70aSDimitry Andric } // end anonymous namespace
1180946e70aSDimitry Andric 
insertThunks(MachineModuleInfo & MMI,MachineFunction & MF,bool ExistingThunks)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 
populateThunk(MachineFunction & MF)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 
createX86IndirectThunksPass()2440946e70aSDimitry Andric FunctionPass *llvm::createX86IndirectThunksPass() {
2450946e70aSDimitry Andric   return new X86IndirectThunks();
2460946e70aSDimitry Andric }
2470946e70aSDimitry Andric 
2480946e70aSDimitry Andric char X86IndirectThunks::ID = 0;
249