1 //===- bolt/Passes/RetpolineInsertion.cpp ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements RetpolineInsertion class, which replaces indirect
10 // branches (calls and jumps) with calls to retpolines to protect against branch
11 // target injection attacks.
12 // A unique retpoline is created for each register holding the address of the
13 // callee, if the callee address is in memory %r11 is used if available to
14 // hold the address of the callee before calling the retpoline, otherwise an
15 // address pattern specific retpoline is called where the callee address is
16 // loaded inside the retpoline.
17 // The user can determine when to assume %r11 available using r11-availability
18 // option, by default %r11 is assumed not available.
19 // Adding lfence instruction to the body of the speculate code is enabled by
20 // default and can be controlled by the user using retpoline-lfence option.
22 //===----------------------------------------------------------------------===//
24 #include "bolt/Passes/RetpolineInsertion.h"
25 #include "llvm/MC/MCInstPrinter.h"
26 #include "llvm/Support/raw_ostream.h"
28 #define DEBUG_TYPE "bolt-retpoline"
34 extern cl::OptionCategory BoltCategory
;
36 llvm::cl::opt
<bool> InsertRetpolines("insert-retpolines",
37 cl::desc("run retpoline insertion pass"),
38 cl::cat(BoltCategory
));
41 RetpolineLfence("retpoline-lfence",
42 cl::desc("determine if lfence instruction should exist in the retpoline"),
46 cl::cat(BoltCategory
));
48 cl::opt
<RetpolineInsertion::AvailabilityOptions
> R11Availability(
50 cl::desc("determine the availability of r11 before indirect branches"),
51 cl::init(RetpolineInsertion::AvailabilityOptions::NEVER
),
52 cl::values(clEnumValN(RetpolineInsertion::AvailabilityOptions::NEVER
,
53 "never", "r11 not available"),
54 clEnumValN(RetpolineInsertion::AvailabilityOptions::ALWAYS
,
55 "always", "r11 avaialable before calls and jumps"),
56 clEnumValN(RetpolineInsertion::AvailabilityOptions::ABI
, "abi",
57 "r11 avaialable before calls but not before jumps")),
58 cl::ZeroOrMore
, cl::cat(BoltCategory
));
65 // Retpoline function structure:
70 // BB2: mov %reg, (%rsp)
78 BinaryFunction
*createNewRetpoline(BinaryContext
&BC
,
79 const std::string
&RetpolineTag
,
80 const IndirectBranchInfo
&BrInfo
,
83 MCContext
&Ctx
= *BC
.Ctx
.get();
84 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Creating a new retpoline function["
85 << RetpolineTag
<< "]\n");
87 BinaryFunction
*NewRetpoline
=
88 BC
.createInjectedBinaryFunction(RetpolineTag
, true);
89 std::vector
<std::unique_ptr
<BinaryBasicBlock
>> NewBlocks(3);
90 for (int I
= 0; I
< 3; I
++) {
92 Ctx
.createNamedTempSymbol(Twine(RetpolineTag
+ "_BB" + to_string(I
)));
93 NewBlocks
[I
] = NewRetpoline
->createBasicBlock(Symbol
);
94 NewBlocks
[I
].get()->setCFIState(0);
97 BinaryBasicBlock
&BB0
= *NewBlocks
[0].get();
98 BinaryBasicBlock
&BB1
= *NewBlocks
[1].get();
99 BinaryBasicBlock
&BB2
= *NewBlocks
[2].get();
101 BB0
.addSuccessor(&BB2
, 0, 0);
102 BB1
.addSuccessor(&BB1
, 0, 0);
106 MIB
.createDirectCall(DirectCall
, BB2
.getLabel(), &Ctx
, /*IsTailCall*/ false);
107 BB0
.addInstruction(DirectCall
);
111 MIB
.createPause(Pause
);
112 BB1
.addInstruction(Pause
);
114 if (opts::RetpolineLfence
) {
116 MIB
.createLfence(Lfence
);
117 BB1
.addInstruction(Lfence
);
120 InstructionListType Seq
;
121 MIB
.createShortJmp(Seq
, BB1
.getLabel(), &Ctx
);
122 BB1
.addInstructions(Seq
.begin(), Seq
.end());
125 if (BrInfo
.isMem()) {
128 MIB
.createSaveToStack(StoreToStack
, MIB
.getStackPointer(), 0,
130 BB2
.addInstruction(StoreToStack
);
133 MIB
.createPushRegister(PushR11
, MIB
.getX86R11(), 8);
134 BB2
.addInstruction(PushR11
);
136 MCInst LoadCalleeAddrs
;
137 const IndirectBranchInfo::MemOpInfo
&MemRef
= BrInfo
.Memory
;
138 MIB
.createLoad(LoadCalleeAddrs
, MemRef
.BaseRegNum
, MemRef
.ScaleImm
,
139 MemRef
.IndexRegNum
, MemRef
.DispImm
, MemRef
.DispExpr
,
140 MemRef
.SegRegNum
, MIB
.getX86R11(), 8);
142 BB2
.addInstruction(LoadCalleeAddrs
);
145 MIB
.createSaveToStack(StoreToStack
, MIB
.getStackPointer(), 8,
147 BB2
.addInstruction(StoreToStack
);
150 MIB
.createPopRegister(PopR11
, MIB
.getX86R11(), 8);
151 BB2
.addInstruction(PopR11
);
153 } else if (BrInfo
.isReg()) {
155 MIB
.createSaveToStack(StoreToStack
, MIB
.getStackPointer(), 0,
156 BrInfo
.BranchReg
, 8);
157 BB2
.addInstruction(StoreToStack
);
159 llvm_unreachable("not expected");
164 MIB
.createReturn(Return
);
165 BB2
.addInstruction(Return
);
166 NewRetpoline
->insertBasicBlocks(nullptr, std::move(NewBlocks
),
167 /* UpdateLayout */ true,
168 /* UpdateCFIState */ false);
170 NewRetpoline
->updateState(BinaryFunction::State::CFG_Finalized
);
174 std::string
createRetpolineFunctionTag(BinaryContext
&BC
,
175 const IndirectBranchInfo
&BrInfo
,
178 llvm::raw_string_ostream
TagOS(Tag
);
179 TagOS
<< "__retpoline_";
181 if (BrInfo
.isReg()) {
182 BC
.InstPrinter
->printRegName(TagOS
, BrInfo
.BranchReg
);
190 return "__retpoline_r11";
192 const IndirectBranchInfo::MemOpInfo
&MemRef
= BrInfo
.Memory
;
196 if (MemRef
.BaseRegNum
!= BC
.MIB
->getNoRegister())
197 BC
.InstPrinter
->printRegName(TagOS
, MemRef
.BaseRegNum
);
201 MemRef
.DispExpr
->print(TagOS
, BC
.AsmInfo
.get());
203 TagOS
<< MemRef
.DispImm
;
205 if (MemRef
.IndexRegNum
!= BC
.MIB
->getNoRegister()) {
206 TagOS
<< "+" << MemRef
.ScaleImm
<< "*";
207 BC
.InstPrinter
->printRegName(TagOS
, MemRef
.IndexRegNum
);
210 if (MemRef
.SegRegNum
!= BC
.MIB
->getNoRegister()) {
212 BC
.InstPrinter
->printRegName(TagOS
, MemRef
.SegRegNum
);
219 BinaryFunction
*RetpolineInsertion::getOrCreateRetpoline(
220 BinaryContext
&BC
, const IndirectBranchInfo
&BrInfo
, bool R11Available
) {
221 const std::string RetpolineTag
=
222 createRetpolineFunctionTag(BC
, BrInfo
, R11Available
);
224 if (CreatedRetpolines
.count(RetpolineTag
))
225 return CreatedRetpolines
[RetpolineTag
];
227 return CreatedRetpolines
[RetpolineTag
] =
228 createNewRetpoline(BC
, RetpolineTag
, BrInfo
, R11Available
);
231 void createBranchReplacement(BinaryContext
&BC
,
232 const IndirectBranchInfo
&BrInfo
,
234 InstructionListType
&Replacement
,
235 const MCSymbol
*RetpolineSymbol
) {
237 // Load the branch address in r11 if available
238 if (BrInfo
.isMem() && R11Available
) {
239 const IndirectBranchInfo::MemOpInfo
&MemRef
= BrInfo
.Memory
;
240 MCInst LoadCalleeAddrs
;
241 MIB
.createLoad(LoadCalleeAddrs
, MemRef
.BaseRegNum
, MemRef
.ScaleImm
,
242 MemRef
.IndexRegNum
, MemRef
.DispImm
, MemRef
.DispExpr
,
243 MemRef
.SegRegNum
, MIB
.getX86R11(), 8);
244 Replacement
.push_back(LoadCalleeAddrs
);
247 // Call the retpoline
248 MCInst RetpolineCall
;
249 MIB
.createDirectCall(RetpolineCall
, RetpolineSymbol
, BC
.Ctx
.get(),
250 BrInfo
.isJump() || BrInfo
.isTailCall());
252 Replacement
.push_back(RetpolineCall
);
255 IndirectBranchInfo::IndirectBranchInfo(MCInst
&Inst
, MCPlusBuilder
&MIB
) {
256 IsCall
= MIB
.isCall(Inst
);
257 IsTailCall
= MIB
.isTailCall(Inst
);
259 if (MIB
.isBranchOnMem(Inst
)) {
261 std::optional
<MCPlusBuilder::X86MemOperand
> MO
=
262 MIB
.evaluateX86MemoryOperand(Inst
);
264 llvm_unreachable("not expected");
266 } else if (MIB
.isBranchOnReg(Inst
)) {
267 assert(MCPlus::getNumPrimeOperands(Inst
) == 1 && "expect 1 operand");
268 BranchReg
= Inst
.getOperand(0).getReg();
270 llvm_unreachable("unexpected instruction");
274 void RetpolineInsertion::runOnFunctions(BinaryContext
&BC
) {
275 if (!opts::InsertRetpolines
)
279 "retpoline insertion not supported for target architecture");
281 assert(BC
.HasRelocations
&& "retpoline mode not supported in non-reloc");
284 uint32_t RetpolinedBranches
= 0;
285 for (auto &It
: BC
.getBinaryFunctions()) {
286 BinaryFunction
&Function
= It
.second
;
287 for (BinaryBasicBlock
&BB
: Function
) {
288 for (auto It
= BB
.begin(); It
!= BB
.end(); ++It
) {
291 if (!MIB
.isIndirectCall(Inst
) && !MIB
.isIndirectBranch(Inst
))
294 IndirectBranchInfo
BrInfo(Inst
, MIB
);
295 bool R11Available
= false;
296 BinaryFunction
*TargetRetpoline
;
297 InstructionListType Replacement
;
299 // Determine if r11 is available before this instruction
300 if (BrInfo
.isMem()) {
301 if (MIB
.hasAnnotation(Inst
, "PLTCall"))
303 else if (opts::R11Availability
== AvailabilityOptions::ALWAYS
)
305 else if (opts::R11Availability
== AvailabilityOptions::ABI
)
306 R11Available
= BrInfo
.isCall();
309 // If the instruction addressing pattern uses rsp and the retpoline
310 // loads the callee address then displacement needs to be updated
311 if (BrInfo
.isMem() && !R11Available
) {
312 IndirectBranchInfo::MemOpInfo
&MemRef
= BrInfo
.Memory
;
313 int Addend
= (BrInfo
.isJump() || BrInfo
.isTailCall()) ? 8 : 16;
314 if (MemRef
.BaseRegNum
== MIB
.getStackPointer())
315 MemRef
.DispImm
+= Addend
;
316 if (MemRef
.IndexRegNum
== MIB
.getStackPointer())
317 MemRef
.DispImm
+= Addend
* MemRef
.ScaleImm
;
320 TargetRetpoline
= getOrCreateRetpoline(BC
, BrInfo
, R11Available
);
322 createBranchReplacement(BC
, BrInfo
, R11Available
, Replacement
,
323 TargetRetpoline
->getSymbol());
325 It
= BB
.replaceInstruction(It
, Replacement
.begin(), Replacement
.end());
326 RetpolinedBranches
++;
330 outs() << "BOLT-INFO: The number of created retpoline functions is : "
331 << CreatedRetpolines
.size()
332 << "\nBOLT-INFO: The number of retpolined branches is : "
333 << RetpolinedBranches
<< "\n";