//===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This pass inserts BTI instructions at the start of every function and basic // block which could be indirectly called. The hardware will (when enabled) // trap when an indirect branch or call instruction targets an instruction // which is not a valid BTI instruction. This is intended to guard against // control-flow hijacking attacks. Note that this does not do anything for RET // instructions, as they can be more precisely protected by return address // signing. // //===----------------------------------------------------------------------===// #include "AArch64Subtarget.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/Support/Debug.h" using namespace llvm; #define DEBUG_TYPE "aarch64-branch-targets" #define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets" namespace { class AArch64BranchTargets : public MachineFunctionPass { public: static char ID; AArch64BranchTargets() : MachineFunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnMachineFunction(MachineFunction &MF) override; StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; } private: void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump); }; } // end anonymous namespace char AArch64BranchTargets::ID = 0; INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets", AARCH64_BRANCH_TARGETS_NAME, false, false) void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const { AU.setPreservesCFG(); MachineFunctionPass::getAnalysisUsage(AU); } FunctionPass *llvm::createAArch64BranchTargetsPass() { return new AArch64BranchTargets(); } bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) { const Function &F = MF.getFunction(); if (!F.hasFnAttribute("branch-target-enforcement")) return false; LLVM_DEBUG( dbgs() << "********** AArch64 Branch Targets **********\n" << "********** Function: " << MF.getName() << '\n'); // LLVM does not consider basic blocks which are the targets of jump tables // to be address-taken (the address can't escape anywhere else), but they are // used for indirect branches, so need BTI instructions. SmallPtrSet JumpTableTargets; if (auto *JTI = MF.getJumpTableInfo()) for (auto &JTE : JTI->getJumpTables()) for (auto *MBB : JTE.MBBs) JumpTableTargets.insert(MBB); bool MadeChange = false; for (MachineBasicBlock &MBB : MF) { bool CouldCall = false, CouldJump = false; // If the function is address-taken or externally-visible, it could be // indirectly called. PLT entries and tail-calls use BR, but when they are // are in guarded pages should all use x16 or x17 to hold the called // address, so we don't need to set CouldJump here. BR instructions in // non-guarded pages (which might be non-BTI-aware code) are allowed to // branch to a "BTI c" using any register. if (&MBB == &*MF.begin() && (F.hasAddressTaken() || !F.hasLocalLinkage())) CouldCall = true; // If the block itself is address-taken, it could be indirectly branched // to, but not called. if (MBB.hasAddressTaken() || JumpTableTargets.count(&MBB)) CouldJump = true; if (CouldCall || CouldJump) { addBTI(MBB, CouldCall, CouldJump); MadeChange = true; } } return MadeChange; } void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump) { LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "") << (CouldCall ? "c" : "") << " to " << MBB.getName() << "\n"); const AArch64InstrInfo *TII = static_cast( MBB.getParent()->getSubtarget().getInstrInfo()); unsigned HintNum = 32; if (CouldCall) HintNum |= 2; if (CouldJump) HintNum |= 4; assert(HintNum != 32 && "No target kinds!"); auto MBBI = MBB.begin(); // Skip the meta instuctions, those will be removed anyway. for (; MBBI != MBB.end() && MBBI->isMetaInstruction(); ++MBBI) ; // PACI[AB]SP are implicitly BTI JC, so no BTI instruction needed there. if (MBBI != MBB.end() && (MBBI->getOpcode() == AArch64::PACIASP || MBBI->getOpcode() == AArch64::PACIBSP)) return; BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()), TII->get(AArch64::HINT)) .addImm(HintNum); }