brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.6 KiB · 57934ae Raw
176 lines · cpp
1//===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==//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 pass inserts BTI instructions at the start of every function and basic10// block which could be indirectly called. The hardware will (when enabled)11// trap when an indirect branch or call instruction targets an instruction12// which is not a valid BTI instruction. This is intended to guard against13// control-flow hijacking attacks. Note that this does not do anything for RET14// instructions, as they can be more precisely protected by return address15// signing.16//17//===----------------------------------------------------------------------===//18 19#include "AArch64MachineFunctionInfo.h"20#include "AArch64Subtarget.h"21#include "Utils/AArch64BaseInfo.h"22#include "llvm/CodeGen/MachineFunctionPass.h"23#include "llvm/CodeGen/MachineInstrBuilder.h"24#include "llvm/CodeGen/MachineJumpTableInfo.h"25#include "llvm/CodeGen/MachineModuleInfo.h"26#include "llvm/Support/Debug.h"27 28using namespace llvm;29 30#define DEBUG_TYPE "aarch64-branch-targets"31#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"32 33namespace {34// BTI HINT encoding: base (32) plus 'c' (2) and/or 'j' (4).35enum : unsigned {36  BTIBase = 32,   // Base immediate for BTI HINT37  BTIC = 1u << 1, // 238  BTIJ = 1u << 2, // 439  BTIMask = BTIC | BTIJ,40};41 42class AArch64BranchTargets : public MachineFunctionPass {43public:44  static char ID;45  AArch64BranchTargets() : MachineFunctionPass(ID) {}46  void getAnalysisUsage(AnalysisUsage &AU) const override;47  bool runOnMachineFunction(MachineFunction &MF) override;48  StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; }49 50private:51  const AArch64Subtarget *Subtarget;52 53  void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump,54              bool NeedsWinCFI);55};56 57} // end anonymous namespace58 59char AArch64BranchTargets::ID = 0;60 61INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets",62                AARCH64_BRANCH_TARGETS_NAME, false, false)63 64void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {65  AU.setPreservesCFG();66  MachineFunctionPass::getAnalysisUsage(AU);67}68 69FunctionPass *llvm::createAArch64BranchTargetsPass() {70  return new AArch64BranchTargets();71}72 73bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {74  if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())75    return false;76 77  LLVM_DEBUG(dbgs() << "********** AArch64 Branch Targets  **********\n"78                    << "********** Function: " << MF.getName() << '\n');79  const Function &F = MF.getFunction();80 81  Subtarget = &MF.getSubtarget<AArch64Subtarget>();82 83  // LLVM does not consider basic blocks which are the targets of jump tables84  // to be address-taken (the address can't escape anywhere else), but they are85  // used for indirect branches, so need BTI instructions.86  SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets;87  if (auto *JTI = MF.getJumpTableInfo())88    for (auto &JTE : JTI->getJumpTables())89      JumpTableTargets.insert_range(JTE.MBBs);90 91  bool MadeChange = false;92  bool HasWinCFI = MF.hasWinCFI();93  for (MachineBasicBlock &MBB : MF) {94    bool CouldCall = false, CouldJump = false;95    // If the function is address-taken or externally-visible, it could be96    // indirectly called. PLT entries and tail-calls use BR, but when they are97    // are in guarded pages should all use x16 or x17 to hold the called98    // address, so we don't need to set CouldJump here. BR instructions in99    // non-guarded pages (which might be non-BTI-aware code) are allowed to100    // branch to a "BTI c" using any register.101    //102    // For ELF targets, this is enough, because AAELF64 says that if the static103    // linker later wants to use an indirect branch instruction in a104    // long-branch thunk, it's also responsible for adding a 'landing pad' with105    // a BTI, and pointing the indirect branch at that. For non-ELF targets we106    // can't rely on that, so we assume that `CouldCall` is _always_ true due107    // to the risk of long-branch thunks at link time.108    if (&MBB == &*MF.begin() && (!Subtarget->isTargetELF() ||109                                 (F.hasAddressTaken() || !F.hasLocalLinkage())))110      CouldCall = true;111 112    // If the block itself is address-taken, it could be indirectly branched113    // to, but not called.114    if (MBB.isMachineBlockAddressTaken() || MBB.isIRBlockAddressTaken() ||115        JumpTableTargets.count(&MBB))116      CouldJump = true;117 118    if (MBB.isEHPad()) {119      if (HasWinCFI && (MBB.isEHFuncletEntry() || MBB.isCleanupFuncletEntry()))120        CouldCall = true;121      else122        CouldJump = true;123    }124    if (CouldCall || CouldJump) {125      addBTI(MBB, CouldCall, CouldJump, HasWinCFI);126      MadeChange = true;127    }128  }129 130  return MadeChange;131}132 133void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,134                                  bool CouldJump, bool HasWinCFI) {135  LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "")136                    << (CouldCall ? "c" : "") << " to " << MBB.getName()137                    << "\n");138 139  unsigned HintNum = getBTIHintNum(CouldCall, CouldJump);140  auto MBBI = MBB.begin();141 142  // If the block starts with EH_LABEL(s), skip them first.143  while (MBBI != MBB.end() && MBBI->isEHLabel()) {144    ++MBBI;145  }146 147  // Skip meta/CFI/etc. (and EMITBKEY) to reach the first executable insn.148  for (; MBBI != MBB.end() &&149         (MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY);150       ++MBBI)151    ;152 153  // PACI[AB]SP are implicitly BTI c so insertion of a BTI can be skipped in154  // this case. Depending on the runtime value of SCTLR_EL1.BT[01], they are not155  // equivalent to a BTI jc, which still requires an additional BTI.156  if (MBBI != MBB.end() && ((HintNum & BTIMask) == BTIC) &&157      (MBBI->getOpcode() == AArch64::PACIASP ||158       MBBI->getOpcode() == AArch64::PACIBSP))159    return;160 161  const AArch64InstrInfo *TII = Subtarget->getInstrInfo();162 163  // Insert BTI exactly at the first executable instruction.164  const DebugLoc DL = MBB.findDebugLoc(MBBI);165  MachineInstr *BTI = BuildMI(MBB, MBBI, DL, TII->get(AArch64::HINT))166                          .addImm(HintNum)167                          .getInstr();168 169  // WinEH: put .seh_nop after BTI when the first real insn is FrameSetup.170  if (HasWinCFI && MBBI != MBB.end() &&171      MBBI->getFlag(MachineInstr::FrameSetup)) {172    auto AfterBTI = std::next(MachineBasicBlock::iterator(BTI));173    BuildMI(MBB, AfterBTI, DL, TII->get(AArch64::SEH_Nop));174  }175}176