Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/X86IndirectThunks.cpp
35266 views
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"
32
#include "llvm/CodeGen/IndirectThunks.h"
33
#include "llvm/CodeGen/MachineFunction.h"
34
#include "llvm/CodeGen/MachineFunctionPass.h"
35
#include "llvm/CodeGen/MachineInstrBuilder.h"
36
#include "llvm/CodeGen/MachineModuleInfo.h"
37
#include "llvm/CodeGen/Passes.h"
38
#include "llvm/CodeGen/TargetPassConfig.h"
39
#include "llvm/IR/IRBuilder.h"
40
#include "llvm/IR/Instructions.h"
41
#include "llvm/IR/Module.h"
42
#include "llvm/Support/CommandLine.h"
43
#include "llvm/Support/Debug.h"
44
#include "llvm/Support/raw_ostream.h"
45
#include "llvm/Target/TargetMachine.h"
46
47
using namespace llvm;
48
49
#define DEBUG_TYPE "x86-retpoline-thunks"
50
51
static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
52
static const char R11RetpolineName[] = "__llvm_retpoline_r11";
53
static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
54
static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
55
static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
56
static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
57
58
static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
59
static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
60
61
namespace {
62
struct 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
}
70
bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,
71
bool ExistingThunks);
72
void populateThunk(MachineFunction &MF);
73
};
74
75
struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
76
const char *getThunkPrefix() { return LVIThunkNamePrefix; }
77
bool mayUseThunk(const MachineFunction &MF) {
78
return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
79
}
80
bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,
81
bool ExistingThunks) {
82
if (ExistingThunks)
83
return false;
84
createThunkFunction(MMI, R11LVIThunkName);
85
return true;
86
}
87
void populateThunk(MachineFunction &MF) {
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
107
class X86IndirectThunks
108
: public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> {
109
public:
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
119
bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI,
120
MachineFunction &MF,
121
bool ExistingThunks) {
122
if (ExistingThunks)
123
return false;
124
if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)
125
createThunkFunction(MMI, R11RetpolineName);
126
else
127
for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
128
EDIRetpolineName})
129
createThunkFunction(MMI, Name);
130
return true;
131
}
132
133
void 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);
193
MachineBasicBlock *Entry = &MF.front();
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
244
FunctionPass *llvm::createX86IndirectThunksPass() {
245
return new X86IndirectThunks();
246
}
247
248
char X86IndirectThunks::ID = 0;
249
250