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