Oliver Stannard | 250e5a5 | 2018-10-08 14:04:24 +0000 | [diff] [blame^] | 1 | //===-- AArch64BranchTargets.cpp -- Harden code using v8.5-A BTI extension -==// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This pass inserts BTI instructions at the start of every function and basic |
| 11 | // block which could be indirectly called. The hardware will (when enabled) |
| 12 | // trap when an indirect branch or call instruction targets an instruction |
| 13 | // which is not a valid BTI instruction. This is intended to guard against |
| 14 | // control-flow hijacking attacks. Note that this does not do anything for RET |
| 15 | // instructions, as they can be more precisely protected by return address |
| 16 | // signing. |
| 17 | // |
| 18 | //===----------------------------------------------------------------------===// |
| 19 | |
| 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 | }; |
| 44 | } // end anonymous namespace |
| 45 | |
| 46 | char AArch64BranchTargets::ID = 0; |
| 47 | |
| 48 | INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets", |
| 49 | AARCH64_BRANCH_TARGETS_NAME, false, false) |
| 50 | |
| 51 | void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const { |
| 52 | AU.setPreservesCFG(); |
| 53 | MachineFunctionPass::getAnalysisUsage(AU); |
| 54 | } |
| 55 | |
| 56 | FunctionPass *llvm::createAArch64BranchTargetsPass() { |
| 57 | return new AArch64BranchTargets(); |
| 58 | } |
| 59 | |
| 60 | bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) { |
| 61 | const Function &F = MF.getFunction(); |
| 62 | if (!F.hasFnAttribute("branch-target-enforcement")) |
| 63 | return false; |
| 64 | |
| 65 | LLVM_DEBUG( |
| 66 | dbgs() << "********** AArch64 Branch Targets **********\n" |
| 67 | << "********** Function: " << MF.getName() << '\n'); |
| 68 | |
| 69 | // LLVM does not consider basic blocks which are the targets of jump tables |
| 70 | // to be address-taken (the address can't escape anywhere else), but they are |
| 71 | // used for indirect branches, so need BTI instructions. |
| 72 | SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets; |
| 73 | if (auto *JTI = MF.getJumpTableInfo()) |
| 74 | for (auto &JTE : JTI->getJumpTables()) |
| 75 | for (auto *MBB : JTE.MBBs) |
| 76 | JumpTableTargets.insert(MBB); |
| 77 | |
| 78 | bool MadeChange = false; |
| 79 | for (MachineBasicBlock &MBB : MF) { |
| 80 | bool CouldCall = false, CouldJump = false; |
| 81 | // If the function is address-taken or externally-visible, it could be |
| 82 | // indirectly called. PLT entries and tail-calls use BR, but when they are |
| 83 | // are in guarded pages should all use x16 or x17 to hold the called |
| 84 | // address, so we don't need to set CouldJump here. BR instructions in |
| 85 | // non-guarded pages (which might be non-BTI-aware code) are allowed to |
| 86 | // branch to a "BTI c" using any register. |
| 87 | if (&MBB == &*MF.begin() && (F.hasAddressTaken() || !F.hasLocalLinkage())) |
| 88 | CouldCall = true; |
| 89 | |
| 90 | // If the block itself is address-taken, it could be indirectly branched |
| 91 | // to, but not called. |
| 92 | if (MBB.hasAddressTaken() || JumpTableTargets.count(&MBB)) |
| 93 | CouldJump = true; |
| 94 | |
| 95 | if (CouldCall || CouldJump) { |
| 96 | addBTI(MBB, CouldCall, CouldJump); |
| 97 | MadeChange = true; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | return MadeChange; |
| 102 | } |
| 103 | |
| 104 | void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall, |
| 105 | bool CouldJump) { |
| 106 | LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "") |
| 107 | << (CouldCall ? "c" : "") << " to " << MBB.getName() |
| 108 | << "\n"); |
| 109 | |
| 110 | const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>( |
| 111 | MBB.getParent()->getSubtarget().getInstrInfo()); |
| 112 | |
| 113 | unsigned HintNum = 32; |
| 114 | if (CouldCall) |
| 115 | HintNum |= 2; |
| 116 | if (CouldJump) |
| 117 | HintNum |= 4; |
| 118 | assert(HintNum != 32 && "No target kinds!"); |
| 119 | |
| 120 | auto MBBI = MBB.begin(); |
| 121 | |
| 122 | // PACI[AB]SP are implicitly BTI JC, so no BTI instruction needed there. |
| 123 | if (MBBI != MBB.end() && (MBBI->getOpcode() == AArch64::PACIASP || |
| 124 | MBBI->getOpcode() == AArch64::PACIBSP)) |
| 125 | return; |
| 126 | |
| 127 | BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()), |
| 128 | TII->get(AArch64::HINT)) |
| 129 | .addImm(HintNum); |
| 130 | } |