[flang][MLIR] Support delayed privatization for `wsloop` (PFT -> MLIR) (#118271)
[llvm-project.git] / llvm / lib / Target / X86 / X86IndirectThunks.cpp
blobc5a5e6e621ffeabf60de627ebd53bdb2d2ea47ef
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 //===----------------------------------------------------------------------===//
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/MachineInstrBuilder.h"
35 #include "llvm/CodeGen/MachineModuleInfo.h"
36 #include "llvm/CodeGen/Passes.h"
37 #include "llvm/CodeGen/TargetPassConfig.h"
38 #include "llvm/IR/Instructions.h"
39 #include "llvm/Target/TargetMachine.h"
41 using namespace llvm;
43 #define DEBUG_TYPE "x86-retpoline-thunks"
45 static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
46 static const char R11RetpolineName[] = "__llvm_retpoline_r11";
47 static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
48 static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
49 static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
50 static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
52 static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
53 static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
55 namespace {
56 struct 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();
64 bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,
65 bool ExistingThunks);
66 void populateThunk(MachineFunction &MF);
69 struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
70 const char *getThunkPrefix() { return LVIThunkNamePrefix; }
71 bool mayUseThunk(const MachineFunction &MF) {
72 return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
74 bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF,
75 bool ExistingThunks) {
76 if (ExistingThunks)
77 return false;
78 createThunkFunction(MMI, R11LVIThunkName);
79 return true;
81 void populateThunk(MachineFunction &MF) {
82 assert (MF.size() == 1);
83 MachineBasicBlock *Entry = &MF.front();
84 Entry->clear();
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);
101 class X86IndirectThunks
102 : public ThunkInserterPass<RetpolineThunkInserter, LVIThunkInserter> {
103 public:
104 static char ID;
106 X86IndirectThunks() : ThunkInserterPass(ID) {}
108 StringRef getPassName() const override { return "X86 Indirect Thunks"; }
111 } // end anonymous namespace
113 bool RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI,
114 MachineFunction &MF,
115 bool ExistingThunks) {
116 if (ExistingThunks)
117 return false;
118 if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)
119 createThunkFunction(MMI, R11RetpolineName);
120 else
121 for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
122 EDIRetpolineName})
123 createThunkFunction(MMI, Name);
124 return true;
127 void 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");
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
159 // __llvm_retpoline_ecx:
160 // ... # Same setup
161 // movl %ecx, (%esp)
162 // retl
164 // __llvm_retpoline_edx:
165 // ... # Same setup
166 // movl %edx, (%esp)
167 // retl
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!");
185 const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
186 assert (MF.size() == 1);
187 MachineBasicBlock *Entry = &MF.front();
188 Entry->clear();
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);
198 const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
199 const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32;
201 Entry->addLiveIn(ThunkReg);
202 BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
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);
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);
223 CallTarget->addLiveIn(ThunkReg);
224 CallTarget->setMachineBlockAddressTaken();
225 CallTarget->setAlignment(Align(16));
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,
232 .addReg(ThunkReg);
234 CallTarget->back().setPreInstrSymbol(MF, TargetSym);
235 BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
238 FunctionPass *llvm::createX86IndirectThunksPass() {
239 return new X86IndirectThunks();
242 char X86IndirectThunks::ID = 0;