llvm-project
338 строк · 11.4 Кб
1//===- bolt/Passes/RetpolineInsertion.cpp ---------------------------------===//
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//
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.
21//
22//===----------------------------------------------------------------------===//
23
24#include "bolt/Passes/RetpolineInsertion.h"25#include "llvm/MC/MCInstPrinter.h"26#include "llvm/Support/raw_ostream.h"27
28#define DEBUG_TYPE "bolt-retpoline"29
30using namespace llvm;31using namespace bolt;32namespace opts {33
34extern cl::OptionCategory BoltCategory;35
36llvm::cl::opt<bool> InsertRetpolines("insert-retpolines",37cl::desc("run retpoline insertion pass"),38cl::cat(BoltCategory));39
40llvm::cl::opt<bool>41RetpolineLfence("retpoline-lfence",42cl::desc("determine if lfence instruction should exist in the retpoline"),43cl::init(true),44cl::ZeroOrMore,45cl::Hidden,46cl::cat(BoltCategory));47
48cl::opt<RetpolineInsertion::AvailabilityOptions> R11Availability(49"r11-availability",50cl::desc("determine the availability of r11 before indirect branches"),51cl::init(RetpolineInsertion::AvailabilityOptions::NEVER),52cl::values(clEnumValN(RetpolineInsertion::AvailabilityOptions::NEVER,53"never", "r11 not available"),54clEnumValN(RetpolineInsertion::AvailabilityOptions::ALWAYS,55"always", "r11 available before calls and jumps"),56clEnumValN(RetpolineInsertion::AvailabilityOptions::ABI, "abi",57"r11 available before calls but not before jumps")),58cl::ZeroOrMore, cl::cat(BoltCategory));59
60} // namespace opts61
62namespace llvm {63namespace bolt {64
65// Retpoline function structure:
66// BB0: call BB2
67// BB1: pause
68// lfence
69// jmp BB1
70// BB2: mov %reg, (%rsp)
71// ret
72// or
73// BB2: push %r11
74// mov Address, %r11
75// mov %r11, 8(%rsp)
76// pop %r11
77// ret
78BinaryFunction *createNewRetpoline(BinaryContext &BC,79const std::string &RetpolineTag,80const IndirectBranchInfo &BrInfo,81bool R11Available) {82auto &MIB = *BC.MIB;83MCContext &Ctx = *BC.Ctx.get();84LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Creating a new retpoline function["85<< RetpolineTag << "]\n");86
87BinaryFunction *NewRetpoline =88BC.createInjectedBinaryFunction(RetpolineTag, true);89std::vector<std::unique_ptr<BinaryBasicBlock>> NewBlocks(3);90for (int I = 0; I < 3; I++) {91MCSymbol *Symbol =92Ctx.createNamedTempSymbol(Twine(RetpolineTag + "_BB" + to_string(I)));93NewBlocks[I] = NewRetpoline->createBasicBlock(Symbol);94NewBlocks[I].get()->setCFIState(0);95}96
97BinaryBasicBlock &BB0 = *NewBlocks[0].get();98BinaryBasicBlock &BB1 = *NewBlocks[1].get();99BinaryBasicBlock &BB2 = *NewBlocks[2].get();100
101BB0.addSuccessor(&BB2, 0, 0);102BB1.addSuccessor(&BB1, 0, 0);103
104// Build BB0105MCInst DirectCall;106MIB.createDirectCall(DirectCall, BB2.getLabel(), &Ctx, /*IsTailCall*/ false);107BB0.addInstruction(DirectCall);108
109// Build BB1110MCInst Pause;111MIB.createPause(Pause);112BB1.addInstruction(Pause);113
114if (opts::RetpolineLfence) {115MCInst Lfence;116MIB.createLfence(Lfence);117BB1.addInstruction(Lfence);118}119
120InstructionListType Seq;121MIB.createShortJmp(Seq, BB1.getLabel(), &Ctx);122BB1.addInstructions(Seq.begin(), Seq.end());123
124// Build BB2125if (BrInfo.isMem()) {126if (R11Available) {127MCInst StoreToStack;128MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 0,129MIB.getX86R11(), 8);130BB2.addInstruction(StoreToStack);131} else {132MCInst PushR11;133MIB.createPushRegister(PushR11, MIB.getX86R11(), 8);134BB2.addInstruction(PushR11);135
136MCInst LoadCalleeAddrs;137const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;138MIB.createLoad(LoadCalleeAddrs, MemRef.BaseRegNum, MemRef.ScaleImm,139MemRef.IndexRegNum, MemRef.DispImm, MemRef.DispExpr,140MemRef.SegRegNum, MIB.getX86R11(), 8);141
142BB2.addInstruction(LoadCalleeAddrs);143
144MCInst StoreToStack;145MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 8,146MIB.getX86R11(), 8);147BB2.addInstruction(StoreToStack);148
149MCInst PopR11;150MIB.createPopRegister(PopR11, MIB.getX86R11(), 8);151BB2.addInstruction(PopR11);152}153} else if (BrInfo.isReg()) {154MCInst StoreToStack;155MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 0,156BrInfo.BranchReg, 8);157BB2.addInstruction(StoreToStack);158} else {159llvm_unreachable("not expected");160}161
162// return163MCInst Return;164MIB.createReturn(Return);165BB2.addInstruction(Return);166NewRetpoline->insertBasicBlocks(nullptr, std::move(NewBlocks),167/* UpdateLayout */ true,168/* UpdateCFIState */ false);169
170NewRetpoline->updateState(BinaryFunction::State::CFG_Finalized);171return NewRetpoline;172}
173
174std::string createRetpolineFunctionTag(BinaryContext &BC,175const IndirectBranchInfo &BrInfo,176bool R11Available) {177std::string Tag;178llvm::raw_string_ostream TagOS(Tag);179TagOS << "__retpoline_";180
181if (BrInfo.isReg()) {182BC.InstPrinter->printRegName(TagOS, BrInfo.BranchReg);183TagOS << "_";184TagOS.flush();185return Tag;186}187
188// Memory Branch189if (R11Available)190return "__retpoline_r11";191
192const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;193
194TagOS << "mem_";195
196if (MemRef.BaseRegNum != BC.MIB->getNoRegister())197BC.InstPrinter->printRegName(TagOS, MemRef.BaseRegNum);198
199TagOS << "+";200if (MemRef.DispExpr)201MemRef.DispExpr->print(TagOS, BC.AsmInfo.get());202else203TagOS << MemRef.DispImm;204
205if (MemRef.IndexRegNum != BC.MIB->getNoRegister()) {206TagOS << "+" << MemRef.ScaleImm << "*";207BC.InstPrinter->printRegName(TagOS, MemRef.IndexRegNum);208}209
210if (MemRef.SegRegNum != BC.MIB->getNoRegister()) {211TagOS << "_seg_";212BC.InstPrinter->printRegName(TagOS, MemRef.SegRegNum);213}214
215TagOS.flush();216return Tag;217}
218
219BinaryFunction *RetpolineInsertion::getOrCreateRetpoline(220BinaryContext &BC, const IndirectBranchInfo &BrInfo, bool R11Available) {221const std::string RetpolineTag =222createRetpolineFunctionTag(BC, BrInfo, R11Available);223
224if (CreatedRetpolines.count(RetpolineTag))225return CreatedRetpolines[RetpolineTag];226
227return CreatedRetpolines[RetpolineTag] =228createNewRetpoline(BC, RetpolineTag, BrInfo, R11Available);229}
230
231void createBranchReplacement(BinaryContext &BC,232const IndirectBranchInfo &BrInfo,233bool R11Available,234InstructionListType &Replacement,235const MCSymbol *RetpolineSymbol) {236auto &MIB = *BC.MIB;237// Load the branch address in r11 if available238if (BrInfo.isMem() && R11Available) {239const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;240MCInst LoadCalleeAddrs;241MIB.createLoad(LoadCalleeAddrs, MemRef.BaseRegNum, MemRef.ScaleImm,242MemRef.IndexRegNum, MemRef.DispImm, MemRef.DispExpr,243MemRef.SegRegNum, MIB.getX86R11(), 8);244Replacement.push_back(LoadCalleeAddrs);245}246
247// Call the retpoline248MCInst RetpolineCall;249MIB.createDirectCall(RetpolineCall, RetpolineSymbol, BC.Ctx.get(),250BrInfo.isJump() || BrInfo.isTailCall());251
252Replacement.push_back(RetpolineCall);253}
254
255IndirectBranchInfo::IndirectBranchInfo(MCInst &Inst, MCPlusBuilder &MIB) {256IsCall = MIB.isCall(Inst);257IsTailCall = MIB.isTailCall(Inst);258
259if (MIB.isBranchOnMem(Inst)) {260IsMem = true;261std::optional<MCPlusBuilder::X86MemOperand> MO =262MIB.evaluateX86MemoryOperand(Inst);263if (!MO)264llvm_unreachable("not expected");265Memory = MO.value();266} else if (MIB.isBranchOnReg(Inst)) {267assert(MCPlus::getNumPrimeOperands(Inst) == 1 && "expect 1 operand");268BranchReg = Inst.getOperand(0).getReg();269} else {270llvm_unreachable("unexpected instruction");271}272}
273
274Error RetpolineInsertion::runOnFunctions(BinaryContext &BC) {275if (!opts::InsertRetpolines)276return Error::success();277
278assert(BC.isX86() &&279"retpoline insertion not supported for target architecture");280
281assert(BC.HasRelocations && "retpoline mode not supported in non-reloc");282
283auto &MIB = *BC.MIB;284uint32_t RetpolinedBranches = 0;285for (auto &It : BC.getBinaryFunctions()) {286BinaryFunction &Function = It.second;287for (BinaryBasicBlock &BB : Function) {288for (auto It = BB.begin(); It != BB.end(); ++It) {289MCInst &Inst = *It;290
291if (!MIB.isIndirectCall(Inst) && !MIB.isIndirectBranch(Inst))292continue;293
294IndirectBranchInfo BrInfo(Inst, MIB);295bool R11Available = false;296BinaryFunction *TargetRetpoline;297InstructionListType Replacement;298
299// Determine if r11 is available before this instruction300if (BrInfo.isMem()) {301if (MIB.hasAnnotation(Inst, "PLTCall"))302R11Available = true;303else if (opts::R11Availability == AvailabilityOptions::ALWAYS)304R11Available = true;305else if (opts::R11Availability == AvailabilityOptions::ABI)306R11Available = BrInfo.isCall();307}308
309// If the instruction addressing pattern uses rsp and the retpoline310// loads the callee address then displacement needs to be updated311if (BrInfo.isMem() && !R11Available) {312IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;313int Addend = (BrInfo.isJump() || BrInfo.isTailCall()) ? 8 : 16;314if (MemRef.BaseRegNum == MIB.getStackPointer())315MemRef.DispImm += Addend;316if (MemRef.IndexRegNum == MIB.getStackPointer())317MemRef.DispImm += Addend * MemRef.ScaleImm;318}319
320TargetRetpoline = getOrCreateRetpoline(BC, BrInfo, R11Available);321
322createBranchReplacement(BC, BrInfo, R11Available, Replacement,323TargetRetpoline->getSymbol());324
325It = BB.replaceInstruction(It, Replacement.begin(), Replacement.end());326RetpolinedBranches++;327}328}329}330BC.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";334return Error::success();335}
336
337} // namespace bolt338} // namespace llvm339