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