| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This pass inserts BTI instructions at the start of every function and basic |
| 10 | // block which could be indirectly called. The hardware will (when enabled) |
| 11 | // trap when an indirect branch or call instruction targets an instruction |
| 12 | // which is not a valid BTI instruction. This is intended to guard against |
| 13 | // control-flow hijacking attacks. Note that this does not do anything for RET |
| 14 | // instructions, as they can be more precisely protected by return address |
| 15 | // signing. |
| 16 | // |
| 17 | //===----------------------------------------------------------------------===// |
| 18 | |
| 19 | #include "AArch64MachineFunctionInfo.h" |
| 20 | #include "AArch64Subtarget.h" |
| 21 | #include "llvm/CodeGen/MachineFunctionPass.h" |
| 22 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
| 23 | #include "llvm/CodeGen/MachineJumpTableInfo.h" |
| 24 | #include "llvm/CodeGen/MachineModuleInfo.h" |
| 25 | #include "llvm/Support/Debug.h" |
| 26 | |
| 27 | using namespace llvm; |
| 28 | |
| 29 | #define DEBUG_TYPE "aarch64-branch-targets" |
| 30 | #define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets" |
| 31 | |
| 32 | namespace { |
| 33 | class AArch64BranchTargets : public MachineFunctionPass { |
| 34 | public: |
| 35 | static char ID; |
| 36 | AArch64BranchTargets() : MachineFunctionPass(ID) {} |
| 37 | void getAnalysisUsage(AnalysisUsage &AU) const override; |
| 38 | bool runOnMachineFunction(MachineFunction &MF) override; |
| 39 | StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; } |
| 40 | |
| 41 | private: |
| 42 | void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump, |
| 43 | bool NeedsWinCFI); |
| 44 | }; |
| 45 | } // end anonymous namespace |
| 46 | |
| 47 | char AArch64BranchTargets::ID = 0; |
| 48 | |
| 49 | INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets" , |
| 50 | AARCH64_BRANCH_TARGETS_NAME, false, false) |
| 51 | |
| 52 | void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const { |
| 53 | AU.setPreservesCFG(); |
| 54 | MachineFunctionPass::getAnalysisUsage(AU); |
| 55 | } |
| 56 | |
| 57 | FunctionPass *llvm::createAArch64BranchTargetsPass() { |
| 58 | return new AArch64BranchTargets(); |
| 59 | } |
| 60 | |
| 61 | bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) { |
| 62 | if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement()) |
| 63 | return false; |
| 64 | |
| 65 | LLVM_DEBUG( |
| 66 | dbgs() << "********** AArch64 Branch Targets **********\n" |
| 67 | << "********** Function: " << MF.getName() << '\n'); |
| 68 | const Function &F = MF.getFunction(); |
| 69 | |
| 70 | // LLVM does not consider basic blocks which are the targets of jump tables |
| 71 | // to be address-taken (the address can't escape anywhere else), but they are |
| 72 | // used for indirect branches, so need BTI instructions. |
| 73 | SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets; |
| 74 | if (auto *JTI = MF.getJumpTableInfo()) |
| 75 | for (auto &JTE : JTI->getJumpTables()) |
| 76 | JumpTableTargets.insert_range(R: JTE.MBBs); |
| 77 | |
| 78 | bool MadeChange = false; |
| 79 | bool HasWinCFI = MF.hasWinCFI(); |
| 80 | for (MachineBasicBlock &MBB : MF) { |
| 81 | bool CouldCall = false, CouldJump = false; |
| 82 | // If the function is address-taken or externally-visible, it could be |
| 83 | // indirectly called. PLT entries and tail-calls use BR, but when they are |
| 84 | // are in guarded pages should all use x16 or x17 to hold the called |
| 85 | // address, so we don't need to set CouldJump here. BR instructions in |
| 86 | // non-guarded pages (which might be non-BTI-aware code) are allowed to |
| 87 | // branch to a "BTI c" using any register. |
| 88 | // |
| 89 | // For ELF targets, this is enough, because AAELF64 says that if the static |
| 90 | // linker later wants to use an indirect branch instruction in a |
| 91 | // long-branch thunk, it's also responsible for adding a 'landing pad' with |
| 92 | // a BTI, and pointing the indirect branch at that. For non-ELF targets we |
| 93 | // can't rely on that, so we assume that `CouldCall` is _always_ true due |
| 94 | // to the risk of long-branch thunks at link time. |
| 95 | if (&MBB == &*MF.begin() && |
| 96 | (!MF.getSubtarget<AArch64Subtarget>().isTargetELF() || |
| 97 | (F.hasAddressTaken() || !F.hasLocalLinkage()))) |
| 98 | CouldCall = true; |
| 99 | |
| 100 | // If the block itself is address-taken, it could be indirectly branched |
| 101 | // to, but not called. |
| 102 | if (MBB.isMachineBlockAddressTaken() || MBB.isIRBlockAddressTaken() || |
| 103 | JumpTableTargets.count(Ptr: &MBB)) |
| 104 | CouldJump = true; |
| 105 | |
| 106 | if (CouldCall || CouldJump) { |
| 107 | addBTI(MBB, CouldCall, CouldJump, NeedsWinCFI: HasWinCFI); |
| 108 | MadeChange = true; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | return MadeChange; |
| 113 | } |
| 114 | |
| 115 | void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall, |
| 116 | bool CouldJump, bool HasWinCFI) { |
| 117 | LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "" ) |
| 118 | << (CouldCall ? "c" : "" ) << " to " << MBB.getName() |
| 119 | << "\n" ); |
| 120 | |
| 121 | const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>( |
| 122 | MBB.getParent()->getSubtarget().getInstrInfo()); |
| 123 | |
| 124 | unsigned HintNum = 32; |
| 125 | if (CouldCall) |
| 126 | HintNum |= 2; |
| 127 | if (CouldJump) |
| 128 | HintNum |= 4; |
| 129 | assert(HintNum != 32 && "No target kinds!" ); |
| 130 | |
| 131 | auto MBBI = MBB.begin(); |
| 132 | |
| 133 | // Skip the meta instructions, those will be removed anyway. |
| 134 | for (; MBBI != MBB.end() && |
| 135 | (MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY); |
| 136 | ++MBBI) |
| 137 | ; |
| 138 | |
| 139 | // SCTLR_EL1.BT[01] is set to 0 by default which means |
| 140 | // PACI[AB]SP are implicitly BTI C so no BTI C instruction is needed there. |
| 141 | if (MBBI != MBB.end() && HintNum == 34 && |
| 142 | (MBBI->getOpcode() == AArch64::PACIASP || |
| 143 | MBBI->getOpcode() == AArch64::PACIBSP)) |
| 144 | return; |
| 145 | |
| 146 | if (HasWinCFI && MBBI->getFlag(Flag: MachineInstr::FrameSetup)) { |
| 147 | BuildMI(BB&: MBB, I: MBB.begin(), MIMD: MBB.findDebugLoc(MBBI: MBB.begin()), |
| 148 | MCID: TII->get(Opcode: AArch64::SEH_Nop)); |
| 149 | } |
| 150 | BuildMI(BB&: MBB, I: MBB.begin(), MIMD: MBB.findDebugLoc(MBBI: MBB.begin()), |
| 151 | MCID: TII->get(Opcode: AArch64::HINT)) |
| 152 | .addImm(Val: HintNum); |
| 153 | } |
| 154 | |