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 | |
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(Ptr: MBB); |
77 | |
78 | bool MadeChange = false; |
79 | bool HasWinCFI = MF.hasWinCFI(); |
80 | for (MachineBasicBlock &MBB : MF) { |
81 | bool CouldCall = false, CouldJump = false; |
82 | // Even in cases where a function has internal linkage and is only called |
83 | // directly in its translation unit, it can still be called indirectly if |
84 | // the linker decides to add a thunk to it for whatever reason (say, for |
85 | // example, if it is finally placed far from its call site and a BL is not |
86 | // long-range enough). PLT entries and tail-calls use BR, but when they are |
87 | // are in guarded pages should all use x16 or x17 to hold the called |
88 | // address, so we don't need to set CouldJump here. BR instructions in |
89 | // non-guarded pages (which might be non-BTI-aware code) are allowed to |
90 | // branch to a "BTI c" using any register. |
91 | if (&MBB == &*MF.begin()) |
92 | CouldCall = true; |
93 | |
94 | // If the block itself is address-taken, it could be indirectly branched |
95 | // to, but not called. |
96 | if (MBB.hasAddressTaken() || JumpTableTargets.count(Ptr: &MBB)) |
97 | CouldJump = true; |
98 | |
99 | if (CouldCall || CouldJump) { |
100 | addBTI(MBB, CouldCall, CouldJump, NeedsWinCFI: HasWinCFI); |
101 | MadeChange = true; |
102 | } |
103 | } |
104 | |
105 | return MadeChange; |
106 | } |
107 | |
108 | void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall, |
109 | bool CouldJump, bool HasWinCFI) { |
110 | LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "" ) |
111 | << (CouldCall ? "c" : "" ) << " to " << MBB.getName() |
112 | << "\n" ); |
113 | |
114 | const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>( |
115 | MBB.getParent()->getSubtarget().getInstrInfo()); |
116 | |
117 | unsigned HintNum = 32; |
118 | if (CouldCall) |
119 | HintNum |= 2; |
120 | if (CouldJump) |
121 | HintNum |= 4; |
122 | assert(HintNum != 32 && "No target kinds!" ); |
123 | |
124 | auto MBBI = MBB.begin(); |
125 | |
126 | // Skip the meta instructions, those will be removed anyway. |
127 | for (; MBBI != MBB.end() && |
128 | (MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY); |
129 | ++MBBI) |
130 | ; |
131 | |
132 | // SCTLR_EL1.BT[01] is set to 0 by default which means |
133 | // PACI[AB]SP are implicitly BTI C so no BTI C instruction is needed there. |
134 | if (MBBI != MBB.end() && HintNum == 34 && |
135 | (MBBI->getOpcode() == AArch64::PACIASP || |
136 | MBBI->getOpcode() == AArch64::PACIBSP)) |
137 | return; |
138 | |
139 | if (HasWinCFI && MBBI->getFlag(Flag: MachineInstr::FrameSetup)) { |
140 | BuildMI(BB&: MBB, I: MBB.begin(), MIMD: MBB.findDebugLoc(MBBI: MBB.begin()), |
141 | MCID: TII->get(Opcode: AArch64::SEH_Nop)); |
142 | } |
143 | BuildMI(BB&: MBB, I: MBB.begin(), MIMD: MBB.findDebugLoc(MBBI: MBB.begin()), |
144 | MCID: TII->get(Opcode: AArch64::HINT)) |
145 | .addImm(Val: HintNum); |
146 | } |
147 | |