335 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements RetpolineInsertion class, which replaces indirect10// branches (calls and jumps) with calls to retpolines to protect against branch11// target injection attacks.12// A unique retpoline is created for each register holding the address of the13// callee, if the callee address is in memory %r11 is used if available to14// hold the address of the callee before calling the retpoline, otherwise an15// address pattern specific retpoline is called where the callee address is16// loaded inside the retpoline.17// The user can determine when to assume %r11 available using r11-availability18// option, by default %r11 is assumed not available.19// Adding lfence instruction to the body of the speculate code is enabled by20// 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 36static llvm::cl::opt<bool>37 InsertRetpolines("insert-retpolines",38 cl::desc("run retpoline insertion pass"),39 cl::cat(BoltCategory));40 41static llvm::cl::opt<bool> RetpolineLfence(42 "retpoline-lfence",43 cl::desc("determine if lfence instruction should exist in the retpoline"),44 cl::init(true), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory));45 46static cl::opt<RetpolineInsertion::AvailabilityOptions> R11Availability(47 "r11-availability",48 cl::desc("determine the availability of r11 before indirect branches"),49 cl::init(RetpolineInsertion::AvailabilityOptions::NEVER),50 cl::values(clEnumValN(RetpolineInsertion::AvailabilityOptions::NEVER,51 "never", "r11 not available"),52 clEnumValN(RetpolineInsertion::AvailabilityOptions::ALWAYS,53 "always", "r11 available before calls and jumps"),54 clEnumValN(RetpolineInsertion::AvailabilityOptions::ABI, "abi",55 "r11 available before calls but not before jumps")),56 cl::ZeroOrMore, cl::cat(BoltCategory));57 58} // namespace opts59 60namespace llvm {61namespace bolt {62 63// Retpoline function structure:64// BB0: call BB265// BB1: pause66// lfence67// jmp BB168// BB2: mov %reg, (%rsp)69// ret70// or71// BB2: push %r1172// mov Address, %r1173// mov %r11, 8(%rsp)74// pop %r1175// ret76BinaryFunction *createNewRetpoline(BinaryContext &BC,77 const std::string &RetpolineTag,78 const IndirectBranchInfo &BrInfo,79 bool R11Available) {80 auto &MIB = *BC.MIB;81 MCContext &Ctx = *BC.Ctx;82 LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Creating a new retpoline function["83 << RetpolineTag << "]\n");84 85 BinaryFunction *NewRetpoline =86 BC.createInjectedBinaryFunction(RetpolineTag, true);87 std::vector<std::unique_ptr<BinaryBasicBlock>> NewBlocks(3);88 for (int I = 0; I < 3; I++) {89 MCSymbol *Symbol =90 Ctx.createNamedTempSymbol(Twine(RetpolineTag + "_BB" + to_string(I)));91 NewBlocks[I] = NewRetpoline->createBasicBlock(Symbol);92 NewBlocks[I].get()->setCFIState(0);93 }94 95 BinaryBasicBlock &BB0 = *NewBlocks[0].get();96 BinaryBasicBlock &BB1 = *NewBlocks[1].get();97 BinaryBasicBlock &BB2 = *NewBlocks[2].get();98 99 BB0.addSuccessor(&BB2, 0, 0);100 BB1.addSuccessor(&BB1, 0, 0);101 102 // Build BB0103 MCInst DirectCall;104 MIB.createDirectCall(DirectCall, BB2.getLabel(), &Ctx, /*IsTailCall*/ false);105 BB0.addInstruction(DirectCall);106 107 // Build BB1108 MCInst Pause;109 MIB.createPause(Pause);110 BB1.addInstruction(Pause);111 112 if (opts::RetpolineLfence) {113 MCInst Lfence;114 MIB.createLfence(Lfence);115 BB1.addInstruction(Lfence);116 }117 118 InstructionListType Seq;119 MIB.createShortJmp(Seq, BB1.getLabel(), &Ctx);120 BB1.addInstructions(Seq.begin(), Seq.end());121 122 // Build BB2123 if (BrInfo.isMem()) {124 if (R11Available) {125 MCInst StoreToStack;126 MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 0,127 MIB.getX86R11(), 8);128 BB2.addInstruction(StoreToStack);129 } else {130 MCInst PushR11;131 MIB.createPushRegister(PushR11, MIB.getX86R11(), 8);132 BB2.addInstruction(PushR11);133 134 MCInst LoadCalleeAddrs;135 const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;136 MIB.createLoad(LoadCalleeAddrs, MemRef.BaseRegNum, MemRef.ScaleImm,137 MemRef.IndexRegNum, MemRef.DispImm, MemRef.DispExpr,138 MemRef.SegRegNum, MIB.getX86R11(), 8);139 140 BB2.addInstruction(LoadCalleeAddrs);141 142 MCInst StoreToStack;143 MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 8,144 MIB.getX86R11(), 8);145 BB2.addInstruction(StoreToStack);146 147 MCInst PopR11;148 MIB.createPopRegister(PopR11, MIB.getX86R11(), 8);149 BB2.addInstruction(PopR11);150 }151 } else if (BrInfo.isReg()) {152 MCInst StoreToStack;153 MIB.createSaveToStack(StoreToStack, MIB.getStackPointer(), 0,154 BrInfo.BranchReg, 8);155 BB2.addInstruction(StoreToStack);156 } else {157 llvm_unreachable("not expected");158 }159 160 // return161 MCInst Return;162 MIB.createReturn(Return);163 BB2.addInstruction(Return);164 NewRetpoline->insertBasicBlocks(nullptr, std::move(NewBlocks),165 /* UpdateLayout */ true,166 /* UpdateCFIState */ false);167 168 NewRetpoline->updateState(BinaryFunction::State::CFG_Finalized);169 return NewRetpoline;170}171 172std::string createRetpolineFunctionTag(BinaryContext &BC,173 const IndirectBranchInfo &BrInfo,174 bool R11Available) {175 std::string Tag;176 llvm::raw_string_ostream TagOS(Tag);177 TagOS << "__retpoline_";178 179 if (BrInfo.isReg()) {180 BC.InstPrinter->printRegName(TagOS, BrInfo.BranchReg);181 TagOS << "_";182 return Tag;183 }184 185 // Memory Branch186 if (R11Available)187 return "__retpoline_r11";188 189 const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;190 191 TagOS << "mem_";192 193 if (MemRef.BaseRegNum != BC.MIB->getNoRegister())194 BC.InstPrinter->printRegName(TagOS, MemRef.BaseRegNum);195 196 TagOS << "+";197 if (MemRef.DispExpr)198 BC.AsmInfo->printExpr(TagOS, *MemRef.DispExpr);199 else200 TagOS << MemRef.DispImm;201 202 if (MemRef.IndexRegNum != BC.MIB->getNoRegister()) {203 TagOS << "+" << MemRef.ScaleImm << "*";204 BC.InstPrinter->printRegName(TagOS, MemRef.IndexRegNum);205 }206 207 if (MemRef.SegRegNum != BC.MIB->getNoRegister()) {208 TagOS << "_seg_";209 BC.InstPrinter->printRegName(TagOS, MemRef.SegRegNum);210 }211 212 return Tag;213}214 215BinaryFunction *RetpolineInsertion::getOrCreateRetpoline(216 BinaryContext &BC, const IndirectBranchInfo &BrInfo, bool R11Available) {217 const std::string RetpolineTag =218 createRetpolineFunctionTag(BC, BrInfo, R11Available);219 220 if (CreatedRetpolines.count(RetpolineTag))221 return CreatedRetpolines[RetpolineTag];222 223 return CreatedRetpolines[RetpolineTag] =224 createNewRetpoline(BC, RetpolineTag, BrInfo, R11Available);225}226 227void createBranchReplacement(BinaryContext &BC,228 const IndirectBranchInfo &BrInfo,229 bool R11Available,230 InstructionListType &Replacement,231 const MCSymbol *RetpolineSymbol) {232 auto &MIB = *BC.MIB;233 // Load the branch address in r11 if available234 if (BrInfo.isMem() && R11Available) {235 const IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;236 MCInst LoadCalleeAddrs;237 MIB.createLoad(LoadCalleeAddrs, MemRef.BaseRegNum, MemRef.ScaleImm,238 MemRef.IndexRegNum, MemRef.DispImm, MemRef.DispExpr,239 MemRef.SegRegNum, MIB.getX86R11(), 8);240 Replacement.push_back(LoadCalleeAddrs);241 }242 243 // Call the retpoline244 MCInst RetpolineCall;245 MIB.createDirectCall(RetpolineCall, RetpolineSymbol, BC.Ctx.get(),246 BrInfo.isJump() || BrInfo.isTailCall());247 248 Replacement.push_back(RetpolineCall);249}250 251IndirectBranchInfo::IndirectBranchInfo(MCInst &Inst, MCPlusBuilder &MIB) {252 IsCall = MIB.isCall(Inst);253 IsTailCall = MIB.isTailCall(Inst);254 255 if (MIB.isBranchOnMem(Inst)) {256 IsMem = true;257 std::optional<MCPlusBuilder::X86MemOperand> MO =258 MIB.evaluateX86MemoryOperand(Inst);259 if (!MO)260 llvm_unreachable("not expected");261 Memory = MO.value();262 } else if (MIB.isBranchOnReg(Inst)) {263 assert(MCPlus::getNumPrimeOperands(Inst) == 1 && "expect 1 operand");264 BranchReg = Inst.getOperand(0).getReg();265 } else {266 llvm_unreachable("unexpected instruction");267 }268}269 270Error RetpolineInsertion::runOnFunctions(BinaryContext &BC) {271 if (!opts::InsertRetpolines)272 return Error::success();273 274 assert(BC.isX86() &&275 "retpoline insertion not supported for target architecture");276 277 assert(BC.HasRelocations && "retpoline mode not supported in non-reloc");278 279 auto &MIB = *BC.MIB;280 uint32_t RetpolinedBranches = 0;281 for (auto &It : BC.getBinaryFunctions()) {282 BinaryFunction &Function = It.second;283 for (BinaryBasicBlock &BB : Function) {284 for (auto It = BB.begin(); It != BB.end(); ++It) {285 MCInst &Inst = *It;286 287 if (!MIB.isIndirectCall(Inst) && !MIB.isIndirectBranch(Inst))288 continue;289 290 IndirectBranchInfo BrInfo(Inst, MIB);291 bool R11Available = false;292 BinaryFunction *TargetRetpoline;293 InstructionListType Replacement;294 295 // Determine if r11 is available before this instruction296 if (BrInfo.isMem()) {297 if (MIB.hasAnnotation(Inst, "PLTCall"))298 R11Available = true;299 else if (opts::R11Availability == AvailabilityOptions::ALWAYS)300 R11Available = true;301 else if (opts::R11Availability == AvailabilityOptions::ABI)302 R11Available = BrInfo.isCall();303 }304 305 // If the instruction addressing pattern uses rsp and the retpoline306 // loads the callee address then displacement needs to be updated307 if (BrInfo.isMem() && !R11Available) {308 IndirectBranchInfo::MemOpInfo &MemRef = BrInfo.Memory;309 int Addend = (BrInfo.isJump() || BrInfo.isTailCall()) ? 8 : 16;310 if (MemRef.BaseRegNum == MIB.getStackPointer())311 MemRef.DispImm += Addend;312 if (MemRef.IndexRegNum == MIB.getStackPointer())313 MemRef.DispImm += Addend * MemRef.ScaleImm;314 }315 316 TargetRetpoline = getOrCreateRetpoline(BC, BrInfo, R11Available);317 318 createBranchReplacement(BC, BrInfo, R11Available, Replacement,319 TargetRetpoline->getSymbol());320 321 It = BB.replaceInstruction(It, Replacement.begin(), Replacement.end());322 RetpolinedBranches++;323 }324 }325 }326 BC.outs() << "BOLT-INFO: The number of created retpoline functions is : "327 << CreatedRetpolines.size()328 << "\nBOLT-INFO: The number of retpolined branches is : "329 << RetpolinedBranches << "\n";330 return Error::success();331}332 333} // namespace bolt334} // namespace llvm335