Path: blob/main/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
35269 views
//===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass inserts BTI instructions at the start of every function and basic9// block which could be indirectly called. The hardware will (when enabled)10// trap when an indirect branch or call instruction targets an instruction11// which is not a valid BTI instruction. This is intended to guard against12// control-flow hijacking attacks. Note that this does not do anything for RET13// instructions, as they can be more precisely protected by return address14// signing.15//16//===----------------------------------------------------------------------===//1718#include "AArch64MachineFunctionInfo.h"19#include "AArch64Subtarget.h"20#include "llvm/CodeGen/MachineFunctionPass.h"21#include "llvm/CodeGen/MachineInstrBuilder.h"22#include "llvm/CodeGen/MachineJumpTableInfo.h"23#include "llvm/CodeGen/MachineModuleInfo.h"24#include "llvm/Support/Debug.h"2526using namespace llvm;2728#define DEBUG_TYPE "aarch64-branch-targets"29#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"3031namespace {32class AArch64BranchTargets : public MachineFunctionPass {33public:34static char ID;35AArch64BranchTargets() : MachineFunctionPass(ID) {}36void getAnalysisUsage(AnalysisUsage &AU) const override;37bool runOnMachineFunction(MachineFunction &MF) override;38StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; }3940private:41void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump,42bool NeedsWinCFI);43};44} // end anonymous namespace4546char AArch64BranchTargets::ID = 0;4748INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets",49AARCH64_BRANCH_TARGETS_NAME, false, false)5051void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {52AU.setPreservesCFG();53MachineFunctionPass::getAnalysisUsage(AU);54}5556FunctionPass *llvm::createAArch64BranchTargetsPass() {57return new AArch64BranchTargets();58}5960bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {61if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())62return false;6364LLVM_DEBUG(65dbgs() << "********** AArch64 Branch Targets **********\n"66<< "********** Function: " << MF.getName() << '\n');6768// LLVM does not consider basic blocks which are the targets of jump tables69// to be address-taken (the address can't escape anywhere else), but they are70// used for indirect branches, so need BTI instructions.71SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets;72if (auto *JTI = MF.getJumpTableInfo())73for (auto &JTE : JTI->getJumpTables())74for (auto *MBB : JTE.MBBs)75JumpTableTargets.insert(MBB);7677bool MadeChange = false;78bool HasWinCFI = MF.hasWinCFI();79for (MachineBasicBlock &MBB : MF) {80bool CouldCall = false, CouldJump = false;81// Even in cases where a function has internal linkage and is only called82// directly in its translation unit, it can still be called indirectly if83// the linker decides to add a thunk to it for whatever reason (say, for84// example, if it is finally placed far from its call site and a BL is not85// long-range enough). PLT entries and tail-calls use BR, but when they are86// are in guarded pages should all use x16 or x17 to hold the called87// address, so we don't need to set CouldJump here. BR instructions in88// non-guarded pages (which might be non-BTI-aware code) are allowed to89// branch to a "BTI c" using any register.90if (&MBB == &*MF.begin())91CouldCall = true;9293// If the block itself is address-taken, it could be indirectly branched94// to, but not called.95if (MBB.hasAddressTaken() || JumpTableTargets.count(&MBB))96CouldJump = true;9798if (CouldCall || CouldJump) {99addBTI(MBB, CouldCall, CouldJump, HasWinCFI);100MadeChange = true;101}102}103104return MadeChange;105}106107void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,108bool CouldJump, bool HasWinCFI) {109LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "")110<< (CouldCall ? "c" : "") << " to " << MBB.getName()111<< "\n");112113const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>(114MBB.getParent()->getSubtarget().getInstrInfo());115116unsigned HintNum = 32;117if (CouldCall)118HintNum |= 2;119if (CouldJump)120HintNum |= 4;121assert(HintNum != 32 && "No target kinds!");122123auto MBBI = MBB.begin();124125// Skip the meta instructions, those will be removed anyway.126for (; MBBI != MBB.end() &&127(MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY);128++MBBI)129;130131// SCTLR_EL1.BT[01] is set to 0 by default which means132// PACI[AB]SP are implicitly BTI C so no BTI C instruction is needed there.133if (MBBI != MBB.end() && HintNum == 34 &&134(MBBI->getOpcode() == AArch64::PACIASP ||135MBBI->getOpcode() == AArch64::PACIBSP))136return;137138if (HasWinCFI && MBBI->getFlag(MachineInstr::FrameSetup)) {139BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()),140TII->get(AArch64::SEH_Nop));141}142BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()),143TII->get(AArch64::HINT))144.addImm(HintNum);145}146147148