| 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 "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 | |
| 28 | using namespace llvm; |
| 29 | |
| 30 | #define DEBUG_TYPE "aarch64-branch-targets" |
| 31 | #define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets" |
| 32 | |
| 33 | namespace { |
| 34 | // BTI HINT encoding: base (32) plus 'c' (2) and/or 'j' (4). |
| 35 | enum : unsigned { |
| 36 | BTIBase = 32, // Base immediate for BTI HINT |
| 37 | BTIC = 1u << 1, // 2 |
| 38 | BTIJ = 1u << 2, // 4 |
| 39 | BTIMask = BTIC | BTIJ, |
| 40 | }; |
| 41 | |
| 42 | class AArch64BranchTargets : public MachineFunctionPass { |
| 43 | public: |
| 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 | |
| 50 | private: |
| 51 | const AArch64Subtarget *Subtarget; |
| 52 | |
| 53 | void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump, |
| 54 | bool NeedsWinCFI); |
| 55 | }; |
| 56 | |
| 57 | } // end anonymous namespace |
| 58 | |
| 59 | char AArch64BranchTargets::ID = 0; |
| 60 | |
| 61 | INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets" , |
| 62 | AARCH64_BRANCH_TARGETS_NAME, false, false) |
| 63 | |
| 64 | void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const { |
| 65 | AU.setPreservesCFG(); |
| 66 | MachineFunctionPass::getAnalysisUsage(AU); |
| 67 | } |
| 68 | |
| 69 | FunctionPass *llvm::createAArch64BranchTargetsPass() { |
| 70 | return new AArch64BranchTargets(); |
| 71 | } |
| 72 | |
| 73 | bool 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 tables |
| 84 | // to be address-taken (the address can't escape anywhere else), but they are |
| 85 | // 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(R: 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 be |
| 96 | // indirectly called. PLT entries and tail-calls use BR, but when they are |
| 97 | // are in guarded pages should all use x16 or x17 to hold the called |
| 98 | // address, so we don't need to set CouldJump here. BR instructions in |
| 99 | // non-guarded pages (which might be non-BTI-aware code) are allowed to |
| 100 | // branch to a "BTI c" using any register. |
| 101 | // |
| 102 | // For ELF targets, this is enough, because AAELF64 says that if the static |
| 103 | // linker later wants to use an indirect branch instruction in a |
| 104 | // long-branch thunk, it's also responsible for adding a 'landing pad' with |
| 105 | // a BTI, and pointing the indirect branch at that. For non-ELF targets we |
| 106 | // can't rely on that, so we assume that `CouldCall` is _always_ true due |
| 107 | // 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 branched |
| 113 | // to, but not called. |
| 114 | if (MBB.isMachineBlockAddressTaken() || MBB.isIRBlockAddressTaken() || |
| 115 | JumpTableTargets.count(Ptr: &MBB)) |
| 116 | CouldJump = true; |
| 117 | |
| 118 | if (MBB.isEHPad()) { |
| 119 | if (HasWinCFI && (MBB.isEHFuncletEntry() || MBB.isCleanupFuncletEntry())) |
| 120 | CouldCall = true; |
| 121 | else |
| 122 | CouldJump = true; |
| 123 | } |
| 124 | if (CouldCall || CouldJump) { |
| 125 | addBTI(MBB, CouldCall, CouldJump, NeedsWinCFI: HasWinCFI); |
| 126 | MadeChange = true; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | return MadeChange; |
| 131 | } |
| 132 | |
| 133 | void 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(CallTarget: CouldCall, JumpTarget: 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 in |
| 154 | // this case. Depending on the runtime value of SCTLR_EL1.BT[01], they are not |
| 155 | // 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(BB&: MBB, I: MBBI, MIMD: DL, MCID: TII->get(Opcode: AArch64::HINT)) |
| 166 | .addImm(Val: 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(Flag: MachineInstr::FrameSetup)) { |
| 172 | auto AfterBTI = std::next(x: MachineBasicBlock::iterator(BTI)); |
| 173 | BuildMI(BB&: MBB, I: AfterBTI, MIMD: DL, MCID: TII->get(Opcode: AArch64::SEH_Nop)); |
| 174 | } |
| 175 | } |
| 176 | |