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"
36#include "llvm/CodeGen/Passes.h"
40
41using namespace llvm;
42
43#define DEBUG_TYPE "x86-retpoline-thunks"
44
45static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
46static const char R11RetpolineName[] = "__llvm_retpoline_r11";
47static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
48static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
49static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
50static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
51
52static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
53static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
54
55namespace {
56struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {
57 const char *getThunkPrefix() { return RetpolineNamePrefix; }
58 bool mayUseThunk(const MachineFunction &MF) {
59 const auto &STI = MF.getSubtarget<X86Subtarget>();
60 return (STI.useRetpolineIndirectCalls() ||
61 STI.useRetpolineIndirectBranches()) &&
62 !STI.useRetpolineExternalThunk();
63 }
65 bool ExistingThunks);
67};
68
69struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
70 const char *getThunkPrefix() { return LVIThunkNamePrefix; }
71 bool mayUseThunk(const MachineFunction &MF) {
72 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
73 }
75 bool ExistingThunks) {
76 if (ExistingThunks)
77 return false;
79 return true;
80 }
82 assert (MF.size() == 1);
83 MachineBasicBlock *Entry = &MF.front();
84 Entry->clear();
85
86 // This code mitigates LVI by replacing each indirect call/jump with a
87 // direct call/jump to a thunk that looks like:
88 // ```
89 // lfence
90 // jmpq *%r11
91 // ```
92 // This ensures that if the value in register %r11 was loaded from memory,
93 // then the value in %r11 is (architecturally) correct prior to the jump.
94 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
95 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE));
96 BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11);
97 MF.front().addLiveIn(X86::R11);
98 }
99};
100
101class X86IndirectThunks
102 : public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> {
103public:
104 static char ID;
105
106 X86IndirectThunks() : ThunkInserterPass(ID) {}
107
108 StringRef getPassName() const override { return "X86 Indirect Thunks"; }
109};
110
111} // end anonymous namespace
112
113bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI,
114 MachineFunction &MF,
115 bool ExistingThunks) {
116 if (ExistingThunks)
117 return false;
119 createThunkFunction(MMI, R11RetpolineName);
120 else
123 createThunkFunction(MMI, Name);
124 return true;
125}
126
127void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {
128 bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;
129 Register ThunkReg;
130 if (Is64Bit) {
131 assert(MF.getName() == "__llvm_retpoline_r11" &&
132 "Should only have an r11 thunk on 64-bit targets");
133
134 // __llvm_retpoline_r11:
135 // callq .Lr11_call_target
136 // .Lr11_capture_spec:
137 // pause
138 // lfence
139 // jmp .Lr11_capture_spec
140 // .align 16
141 // .Lr11_call_target:
142 // movq %r11, (%rsp)
143 // retq
144 ThunkReg = X86::R11;
145 } else {
146 // For 32-bit targets we need to emit a collection of thunks for various
147 // possible scratch registers as well as a fallback that uses EDI, which is
148 // normally callee saved.
149 // __llvm_retpoline_eax:
150 // calll .Leax_call_target
151 // .Leax_capture_spec:
152 // pause
153 // jmp .Leax_capture_spec
154 // .align 16
155 // .Leax_call_target:
156 // movl %eax, (%esp) # Clobber return addr
157 // retl
158 //
159 // __llvm_retpoline_ecx:
160 // ... # Same setup
161 // movl %ecx, (%esp)
162 // retl
163 //
164 // __llvm_retpoline_edx:
165 // ... # Same setup
166 // movl %edx, (%esp)
167 // retl
168 //
169 // __llvm_retpoline_edi:
170 // ... # Same setup
171 // movl %edi, (%esp)
172 // retl
173 if (MF.getName() == EAXRetpolineName)
174 ThunkReg = X86::EAX;
175 else if (MF.getName() == ECXRetpolineName)
176 ThunkReg = X86::ECX;
177 else if (MF.getName() == EDXRetpolineName)
178 ThunkReg = X86::EDX;
179 else if (MF.getName() == EDIRetpolineName)
180 ThunkReg = X86::EDI;
181 else
182 llvm_unreachable("Invalid thunk name on x86-32!");
183 }
184
185 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
186 assert (MF.size() == 1);
188 Entry->clear();
189
190 MachineBasicBlock *CaptureSpec =
191 MF.CreateMachineBasicBlock(Entry->getBasicBlock());
192 MachineBasicBlock *CallTarget =
193 MF.CreateMachineBasicBlock(Entry->getBasicBlock());
194 MCSymbol *TargetSym = MF.getContext().createTempSymbol();
195 MF.push_back(CaptureSpec);
196 MF.push_back(CallTarget);
197
198 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
199 const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32;
200
201 Entry->addLiveIn(ThunkReg);
202 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
203
204 // The MIR verifier thinks that the CALL in the entry block will fall through
205 // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is
206 // the successor, but the MIR verifier doesn't know how to cope with that.
207 Entry->addSuccessor(CaptureSpec);
208
209 // In the capture loop for speculation, we want to stop the processor from
210 // speculating as fast as possible. On Intel processors, the PAUSE instruction
211 // will block speculation without consuming any execution resources. On AMD
212 // processors, the PAUSE instruction is (essentially) a nop, so we also use an
213 // LFENCE instruction which they have advised will stop speculation as well
214 // with minimal resource utilization. We still end the capture with a jump to
215 // form an infinite loop to fully guarantee that no matter what implementation
216 // of the x86 ISA, speculating this code path never escapes.
217 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE));
218 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE));
219 BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec);
220 CaptureSpec->setMachineBlockAddressTaken();
221 CaptureSpec->addSuccessor(CaptureSpec);
222
223 CallTarget->addLiveIn(ThunkReg);
224 CallTarget->setMachineBlockAddressTaken();
225 CallTarget->setAlignment(Align(16));
226
227 // Insert return address clobber
228 const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
229 const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
230 addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,
231 0)
232 .addReg(ThunkReg);
233
234 CallTarget->back().setPreInstrSymbol(MF, TargetSym);
235 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
236}
237
239 return new X86IndirectThunks();
240}
241
242char 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...
static constexpr Register SPReg
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:345
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 MachineBasicBlock & front() const
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
const TargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
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 TargetMachine & 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:51
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:383
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ Entry
Definition: COFF.h:844
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