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 | |