LLVM 20.0.0git
X86IndirectThunks.cpp
Go to the documentation of this file.
1//==- X86IndirectThunks.cpp - Construct indirect call/jump thunks for x86 --=//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// Pass that injects an MI thunk that is used to lower indirect calls in a way
11/// that prevents speculation on some x86 processors and can be used to mitigate
12/// security vulnerabilities due to targeted speculative execution and side
13/// channels such as CVE-2017-5715.
14///
15/// Currently supported thunks include:
16/// - Retpoline -- A RET-implemented trampoline that lowers indirect calls
17/// - LVI Thunk -- A CALL/JMP-implemented thunk that forces load serialization
18/// before making an indirect call/jump
19///
20/// Note that the reason that this is implemented as a MachineFunctionPass and
21/// not a ModulePass is that ModulePasses at this point in the LLVM X86 pipeline
22/// serialize all transformations, which can consume lots of memory.
23///
24/// TODO(chandlerc): All of this code could use better comments and
25/// documentation.
26///
27//===----------------------------------------------------------------------===//
28
29#include "X86.h"
30#include "X86InstrBuilder.h"
31#include "X86Subtarget.h"
37#include "llvm/CodeGen/Passes.h"
39#include "llvm/IR/IRBuilder.h"
41#include "llvm/IR/Module.h"
43#include "llvm/Support/Debug.h"
46
47using namespace llvm;
48
49#define DEBUG_TYPE "x86-retpoline-thunks"
50
51static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
52static const char R11RetpolineName[] = "__llvm_retpoline_r11";
53static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
54static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
55static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
56static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
57
58static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
59static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
60
61namespace {
62struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {
63 const char *getThunkPrefix() { return RetpolineNamePrefix; }
64 bool mayUseThunk(const MachineFunction &MF) {
65 const auto &STI = MF.getSubtarget<X86Subtarget>();
66 return (STI.useRetpolineIndirectCalls() ||
67 STI.useRetpolineIndirectBranches()) &&
68 !STI.useRetpolineExternalThunk();
69 }
71 bool ExistingThunks);
73};
74
75struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
76 const char *getThunkPrefix() { return LVIThunkNamePrefix; }
77 bool mayUseThunk(const MachineFunction &MF) {
78 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
79 }
81 bool ExistingThunks) {
82 if (ExistingThunks)
83 return false;
85 return true;
86 }
88 assert (MF.size() == 1);
89 MachineBasicBlock *Entry = &MF.front();
90 Entry->clear();
91
92 // This code mitigates LVI by replacing each indirect call/jump with a
93 // direct call/jump to a thunk that looks like:
94 // ```
95 // lfence
96 // jmpq *%r11
97 // ```
98 // This ensures that if the value in register %r11 was loaded from memory,
99 // then the value in %r11 is (architecturally) correct prior to the jump.
100 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
101 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE));
102 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11);
103 MF.front().addLiveIn(X86::R11);
104 }
105};
106
107class X86IndirectThunks
108 : public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> {
109public:
110 static char ID;
111
112 X86IndirectThunks() : ThunkInserterPass(ID) {}
113
114 StringRef getPassName() const override { return "X86 Indirect Thunks"; }
115};
116
117} // end anonymous namespace
118
119bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI,
120 MachineFunction &MF,
121 bool ExistingThunks) {
122 if (ExistingThunks)
123 return false;
125 createThunkFunction(MMI, R11RetpolineName);
126 else
129 createThunkFunction(MMI, Name);
130 return true;
131}
132
133void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {
134 bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;
135 Register ThunkReg;
136 if (Is64Bit) {
137 assert(MF.getName() == "__llvm_retpoline_r11" &&
138 "Should only have an r11 thunk on 64-bit targets");
139
140 // __llvm_retpoline_r11:
141 // callq .Lr11_call_target
142 // .Lr11_capture_spec:
143 // pause
144 // lfence
145 // jmp .Lr11_capture_spec
146 // .align 16
147 // .Lr11_call_target:
148 // movq %r11, (%rsp)
149 // retq
150 ThunkReg = X86::R11;
151 } else {
152 // For 32-bit targets we need to emit a collection of thunks for various
153 // possible scratch registers as well as a fallback that uses EDI, which is
154 // normally callee saved.
155 // __llvm_retpoline_eax:
156 // calll .Leax_call_target
157 // .Leax_capture_spec:
158 // pause
159 // jmp .Leax_capture_spec
160 // .align 16
161 // .Leax_call_target:
162 // movl %eax, (%esp) # Clobber return addr
163 // retl
164 //
165 // __llvm_retpoline_ecx:
166 // ... # Same setup
167 // movl %ecx, (%esp)
168 // retl
169 //
170 // __llvm_retpoline_edx:
171 // ... # Same setup
172 // movl %edx, (%esp)
173 // retl
174 //
175 // __llvm_retpoline_edi:
176 // ... # Same setup
177 // movl %edi, (%esp)
178 // retl
179 if (MF.getName() == EAXRetpolineName)
180 ThunkReg = X86::EAX;
181 else if (MF.getName() == ECXRetpolineName)
182 ThunkReg = X86::ECX;
183 else if (MF.getName() == EDXRetpolineName)
184 ThunkReg = X86::EDX;
185 else if (MF.getName() == EDIRetpolineName)
186 ThunkReg = X86::EDI;
187 else
188 llvm_unreachable("Invalid thunk name on x86-32!");
189 }
190
191 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
192 assert (MF.size() == 1);
194 Entry->clear();
195
196 MachineBasicBlock *CaptureSpec =
197 MF.CreateMachineBasicBlock(Entry->getBasicBlock());
198 MachineBasicBlock *CallTarget =
199 MF.CreateMachineBasicBlock(Entry->getBasicBlock());
200 MCSymbol *TargetSym = MF.getContext().createTempSymbol();
201 MF.push_back(CaptureSpec);
202 MF.push_back(CallTarget);
203
204 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
205 const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32;
206
207 Entry->addLiveIn(ThunkReg);
208 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
209
210 // The MIR verifier thinks that the CALL in the entry block will fall through
211 // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is
212 // the successor, but the MIR verifier doesn't know how to cope with that.
213 Entry->addSuccessor(CaptureSpec);
214
215 // In the capture loop for speculation, we want to stop the processor from
216 // speculating as fast as possible. On Intel processors, the PAUSE instruction
217 // will block speculation without consuming any execution resources. On AMD
218 // processors, the PAUSE instruction is (essentially) a nop, so we also use an
219 // LFENCE instruction which they have advised will stop speculation as well
220 // with minimal resource utilization. We still end the capture with a jump to
221 // form an infinite loop to fully guarantee that no matter what implementation
222 // of the x86 ISA, speculating this code path never escapes.
223 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE));
224 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE));
225 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec);
226 CaptureSpec->setMachineBlockAddressTaken();
227 CaptureSpec->addSuccessor(CaptureSpec);
228
229 CallTarget->addLiveIn(ThunkReg);
230 CallTarget->setMachineBlockAddressTaken();
231 CallTarget->setAlignment(Align(16));
232
233 // Insert return address clobber
234 const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
235 const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
236 addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,
237 0)
238 .addReg(ThunkReg);
239
240 CallTarget->back().setPreInstrSymbol(MF, TargetSym);
241 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
242}
243
245 return new X86IndirectThunks();
246}
247
248char X86IndirectThunks::ID = 0;
std::string Name
const HexagonInstrInfo * TII
Contains a base ThunkInserter class that simplifies injection of MI thunks as well as a default imple...
Module.h This file contains the declarations for the Module class.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Target-Independent Code Generator Pass Configuration Options pass.
static const char ECXRetpolineName[]
static const char RetpolineNamePrefix[]
static const char EDIRetpolineName[]
static const char LVIThunkNamePrefix[]
static const char EDXRetpolineName[]
static const char EAXRetpolineName[]
static const char R11LVIThunkName[]
static const char R11RetpolineName[]
A debug info location.
Definition: DebugLoc.h:33
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:310
MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Definition: MCContext.cpp:346
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:41
void setAlignment(Align A)
Set alignment of the basic block.
void addSuccessor(MachineBasicBlock *Succ, BranchProbability Prob=BranchProbability::getUnknown())
Add Succ as a successor of this MachineBasicBlock.
void addLiveIn(MCRegister PhysReg, LaneBitmask LaneMask=LaneBitmask::getAll())
Adds the specified register as a live in.
void setMachineBlockAddressTaken()
Set this block to indicate that its address is used as something other than the target of a terminato...
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
void push_back(MachineBasicBlock *MBB)
MCContext & getContext() const
unsigned size() const
const LLVMTargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
const MachineBasicBlock & front() const
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
void setPreInstrSymbol(MachineFunction &MF, MCSymbol *Symbol)
Set a symbol that will be emitted just prior to the instruction itself.
This class contains meta information specific to a module.
const LLVMTargetMachine & getTarget() const
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:81
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
TargetInstrInfo - Interface to description of machine instruction set.
const Triple & getTargetTriple() const
Basic implementation of MachineFunctionPass wrapping one or more ThunkInserters passed as type parame...
This class assists in inserting MI thunk functions into the module and rewriting the existing machine...
bool mayUseThunk(const MachineFunction &MF)
Checks if MF may use thunks (true - maybe, false - definitely not).
void createThunkFunction(MachineModuleInfo &MMI, StringRef Name, bool Comdat=true, StringRef TargetAttrs="")
Create an empty thunk function.
InsertedThunksTy insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, InsertedThunksTy ExistingThunks)
Rewrites the function if necessary, returns the set of thunks added.
const char * getThunkPrefix()
Returns common prefix for thunk function's names.
void populateThunk(MachineFunction &MF)
Populate the thunk function with instructions.
ArchType getArch() const
Get the parsed architecture type of this triple.
Definition: Triple.h:373
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ Entry
Definition: COFF.h:826
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
static const MachineInstrBuilder & addRegOffset(const MachineInstrBuilder &MIB, unsigned Reg, bool isKill, int Offset)
addRegOffset - This function is used to add a memory reference of the form [Reg + Offset],...
FunctionPass * createX86IndirectThunksPass()
This pass creates the thunks for the retpoline feature.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39