Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/X86IndirectThunks.cpp
35266 views
//==- X86IndirectThunks.cpp - Construct indirect call/jump thunks for x86 --=//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7/// \file8///9/// Pass that injects an MI thunk that is used to lower indirect calls in a way10/// that prevents speculation on some x86 processors and can be used to mitigate11/// security vulnerabilities due to targeted speculative execution and side12/// channels such as CVE-2017-5715.13///14/// Currently supported thunks include:15/// - Retpoline -- A RET-implemented trampoline that lowers indirect calls16/// - LVI Thunk -- A CALL/JMP-implemented thunk that forces load serialization17/// before making an indirect call/jump18///19/// Note that the reason that this is implemented as a MachineFunctionPass and20/// not a ModulePass is that ModulePasses at this point in the LLVM X86 pipeline21/// serialize all transformations, which can consume lots of memory.22///23/// TODO(chandlerc): All of this code could use better comments and24/// documentation.25///26//===----------------------------------------------------------------------===//2728#include "X86.h"29#include "X86InstrBuilder.h"30#include "X86Subtarget.h"31#include "llvm/CodeGen/IndirectThunks.h"32#include "llvm/CodeGen/MachineFunction.h"33#include "llvm/CodeGen/MachineFunctionPass.h"34#include "llvm/CodeGen/MachineInstrBuilder.h"35#include "llvm/CodeGen/MachineModuleInfo.h"36#include "llvm/CodeGen/Passes.h"37#include "llvm/CodeGen/TargetPassConfig.h"38#include "llvm/IR/IRBuilder.h"39#include "llvm/IR/Instructions.h"40#include "llvm/IR/Module.h"41#include "llvm/Support/CommandLine.h"42#include "llvm/Support/Debug.h"43#include "llvm/Support/raw_ostream.h"44#include "llvm/Target/TargetMachine.h"4546using namespace llvm;4748#define DEBUG_TYPE "x86-retpoline-thunks"4950static const char RetpolineNamePrefix[] = "__llvm_retpoline_";51static const char R11RetpolineName[] = "__llvm_retpoline_r11";52static const char EAXRetpolineName[] = "__llvm_retpoline_eax";53static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";54static const char EDXRetpolineName[] = "__llvm_retpoline_edx";55static const char EDIRetpolineName[] = "__llvm_retpoline_edi";5657static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";58static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";5960namespace {61struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {62const char *getThunkPrefix() { return RetpolineNamePrefix; }63bool mayUseThunk(const MachineFunction &MF) {64const auto &STI = MF.getSubtarget<X86Subtarget>();65return (STI.useRetpolineIndirectCalls() ||66STI.useRetpolineIndirectBranches()) &&67!STI.useRetpolineExternalThunk();68}69bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,70bool ExistingThunks);71void populateThunk(MachineFunction &MF);72};7374struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {75const char *getThunkPrefix() { return LVIThunkNamePrefix; }76bool mayUseThunk(const MachineFunction &MF) {77return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();78}79bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,80bool ExistingThunks) {81if (ExistingThunks)82return false;83createThunkFunction(MMI, R11LVIThunkName);84return true;85}86void populateThunk(MachineFunction &MF) {87assert (MF.size() == 1);88MachineBasicBlock *Entry = &MF.front();89Entry->clear();9091// This code mitigates LVI by replacing each indirect call/jump with a92// direct call/jump to a thunk that looks like:93// ```94// lfence95// jmpq *%r1196// ```97// This ensures that if the value in register %r11 was loaded from memory,98// then the value in %r11 is (architecturally) correct prior to the jump.99const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();100BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE));101BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11);102MF.front().addLiveIn(X86::R11);103}104};105106class X86IndirectThunks107: public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> {108public:109static char ID;110111X86IndirectThunks() : ThunkInserterPass(ID) {}112113StringRef getPassName() const override { return "X86 Indirect Thunks"; }114};115116} // end anonymous namespace117118bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI,119MachineFunction &MF,120bool ExistingThunks) {121if (ExistingThunks)122return false;123if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)124createThunkFunction(MMI, R11RetpolineName);125else126for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,127EDIRetpolineName})128createThunkFunction(MMI, Name);129return true;130}131132void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {133bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;134Register ThunkReg;135if (Is64Bit) {136assert(MF.getName() == "__llvm_retpoline_r11" &&137"Should only have an r11 thunk on 64-bit targets");138139// __llvm_retpoline_r11:140// callq .Lr11_call_target141// .Lr11_capture_spec:142// pause143// lfence144// jmp .Lr11_capture_spec145// .align 16146// .Lr11_call_target:147// movq %r11, (%rsp)148// retq149ThunkReg = X86::R11;150} else {151// For 32-bit targets we need to emit a collection of thunks for various152// possible scratch registers as well as a fallback that uses EDI, which is153// normally callee saved.154// __llvm_retpoline_eax:155// calll .Leax_call_target156// .Leax_capture_spec:157// pause158// jmp .Leax_capture_spec159// .align 16160// .Leax_call_target:161// movl %eax, (%esp) # Clobber return addr162// retl163//164// __llvm_retpoline_ecx:165// ... # Same setup166// movl %ecx, (%esp)167// retl168//169// __llvm_retpoline_edx:170// ... # Same setup171// movl %edx, (%esp)172// retl173//174// __llvm_retpoline_edi:175// ... # Same setup176// movl %edi, (%esp)177// retl178if (MF.getName() == EAXRetpolineName)179ThunkReg = X86::EAX;180else if (MF.getName() == ECXRetpolineName)181ThunkReg = X86::ECX;182else if (MF.getName() == EDXRetpolineName)183ThunkReg = X86::EDX;184else if (MF.getName() == EDIRetpolineName)185ThunkReg = X86::EDI;186else187llvm_unreachable("Invalid thunk name on x86-32!");188}189190const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();191assert (MF.size() == 1);192MachineBasicBlock *Entry = &MF.front();193Entry->clear();194195MachineBasicBlock *CaptureSpec =196MF.CreateMachineBasicBlock(Entry->getBasicBlock());197MachineBasicBlock *CallTarget =198MF.CreateMachineBasicBlock(Entry->getBasicBlock());199MCSymbol *TargetSym = MF.getContext().createTempSymbol();200MF.push_back(CaptureSpec);201MF.push_back(CallTarget);202203const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;204const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32;205206Entry->addLiveIn(ThunkReg);207BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);208209// The MIR verifier thinks that the CALL in the entry block will fall through210// to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is211// the successor, but the MIR verifier doesn't know how to cope with that.212Entry->addSuccessor(CaptureSpec);213214// In the capture loop for speculation, we want to stop the processor from215// speculating as fast as possible. On Intel processors, the PAUSE instruction216// will block speculation without consuming any execution resources. On AMD217// processors, the PAUSE instruction is (essentially) a nop, so we also use an218// LFENCE instruction which they have advised will stop speculation as well219// with minimal resource utilization. We still end the capture with a jump to220// form an infinite loop to fully guarantee that no matter what implementation221// of the x86 ISA, speculating this code path never escapes.222BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE));223BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE));224BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec);225CaptureSpec->setMachineBlockAddressTaken();226CaptureSpec->addSuccessor(CaptureSpec);227228CallTarget->addLiveIn(ThunkReg);229CallTarget->setMachineBlockAddressTaken();230CallTarget->setAlignment(Align(16));231232// Insert return address clobber233const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;234const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;235addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,2360)237.addReg(ThunkReg);238239CallTarget->back().setPreInstrSymbol(MF, TargetSym);240BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));241}242243FunctionPass *llvm::createX86IndirectThunksPass() {244return new X86IndirectThunks();245}246247char X86IndirectThunks::ID = 0;248249250