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
28using namespace llvm;
29
30#define DEBUG_TYPE "aarch64-branch-targets"
31#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"
32
33namespace {
34// BTI HINT encoding: base (32) plus 'c' (2) and/or 'j' (4).
35enum : 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
42class AArch64BranchTargets : public MachineFunctionPass {
43public:
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
50private:
51 const AArch64Subtarget *Subtarget;
52
53 void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump,
54 bool NeedsWinCFI);
55};
56
57} // end anonymous namespace
58
59char AArch64BranchTargets::ID = 0;
60
61INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets",
62 AARCH64_BRANCH_TARGETS_NAME, false, false)
63
64void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {
65 AU.setPreservesCFG();
66 MachineFunctionPass::getAnalysisUsage(AU);
67}
68
69FunctionPass *llvm::createAArch64BranchTargetsPass() {
70 return new AArch64BranchTargets();
71}
72
73bool 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
133void 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