LLVM 22.0.0git
AArch64BranchTargets.cpp
Go to the documentation of this file.
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
20#include "AArch64Subtarget.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();
67}
68
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(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.
115 JumpTableTargets.count(&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, 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(CouldCall, 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.
165 MachineInstr *BTI = BuildMI(MBB, MBBI, DL, TII->get(AArch64::HINT))
166 .addImm(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(MachineInstr::FrameSetup)) {
172 auto AfterBTI = std::next(MachineBasicBlock::iterator(BTI));
173 BuildMI(MBB, AfterBTI, DL, TII->get(AArch64::SEH_Nop));
174 }
175}
#define AARCH64_BRANCH_TARGETS_NAME
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
MachineBasicBlock MachineBasicBlock::iterator MBBI
const HexagonInstrInfo * TII
#define F(x, y, z)
Definition MD5.cpp:54
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
#define LLVM_DEBUG(...)
Definition Debug.h:114
const AArch64InstrInfo * getInstrInfo() const override
Represent the analysis usage information of a pass.
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
bool isEHPad() const
Returns true if the block is a landing pad.
bool isIRBlockAddressTaken() const
Test whether this block is the target of an IR BlockAddress.
bool isEHFuncletEntry() const
Returns true if this is the entry block of an EH funclet.
LLVM_ABI DebugLoc findDebugLoc(instr_iterator MBBI)
Find the next valid DebugLoc starting at MBBI, skipping any debug instructions.
bool isMachineBlockAddressTaken() const
Test whether this block is used as something other than the target of a terminator,...
MachineInstrBundleIterator< MachineInstr > iterator
LLVM_ABI StringRef getName() const
Return the name of the corresponding LLVM basic block, or an empty string.
bool isCleanupFuncletEntry() const
Returns true if this is the entry block of a cleanup funclet.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineJumpTableInfo * getJumpTableInfo() const
getJumpTableInfo - Return the jump table info object for the current function.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
MachineInstr * getInstr() const
If conversion operators fail, use this method to get the MachineInstr explicitly.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
static unsigned getBTIHintNum(bool CallTarget, bool JumpTarget)
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
FunctionPass * createAArch64BranchTargetsPass()