brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.4 KiB · bda2620 Raw
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