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"
25#include "llvm/Support/Debug.h"
26
27using namespace llvm;
28
29#define DEBUG_TYPE "aarch64-branch-targets"
30#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"
31
32namespace {
33// BTI HINT encoding: base (32) plus 'c' (2) and/or 'j' (4).
34enum : unsigned {
35 BTIBase = 32, // Base immediate for BTI HINT
36 BTIC = 1u << 1, // 2
37 BTIJ = 1u << 2, // 4
38 BTIMask = BTIC | BTIJ,
39};
40
41class AArch64BranchTargets : public MachineFunctionPass {
42public:
43 static char ID;
44 AArch64BranchTargets() : MachineFunctionPass(ID) {}
45 void getAnalysisUsage(AnalysisUsage &AU) const override;
46 bool runOnMachineFunction(MachineFunction &MF) override;
47 StringRef getPassName() const override { return AARCH64_BRANCH_TARGETS_NAME; }
48
49private:
50 void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump,
51 bool NeedsWinCFI);
52};
53
54} // end anonymous namespace
55
56char AArch64BranchTargets::ID = 0;
57
58INITIALIZE_PASS(AArch64BranchTargets, "aarch64-branch-targets",
59 AARCH64_BRANCH_TARGETS_NAME, false, false)
60
61void AArch64BranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {
62 AU.setPreservesCFG();
64}
65
67 return new AArch64BranchTargets();
68}
69
70bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
71 if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
72 return false;
73
74 LLVM_DEBUG(dbgs() << "********** AArch64 Branch Targets **********\n"
75 << "********** Function: " << MF.getName() << '\n');
76 const Function &F = MF.getFunction();
77
78 // LLVM does not consider basic blocks which are the targets of jump tables
79 // to be address-taken (the address can't escape anywhere else), but they are
80 // used for indirect branches, so need BTI instructions.
81 SmallPtrSet<MachineBasicBlock *, 8> JumpTableTargets;
82 if (auto *JTI = MF.getJumpTableInfo())
83 for (auto &JTE : JTI->getJumpTables())
84 JumpTableTargets.insert_range(JTE.MBBs);
85
86 bool MadeChange = false;
87 bool HasWinCFI = MF.hasWinCFI();
88 for (MachineBasicBlock &MBB : MF) {
89 bool CouldCall = false, CouldJump = false;
90 // If the function is address-taken or externally-visible, it could be
91 // indirectly called. PLT entries and tail-calls use BR, but when they are
92 // are in guarded pages should all use x16 or x17 to hold the called
93 // address, so we don't need to set CouldJump here. BR instructions in
94 // non-guarded pages (which might be non-BTI-aware code) are allowed to
95 // branch to a "BTI c" using any register.
96 //
97 // For ELF targets, this is enough, because AAELF64 says that if the static
98 // linker later wants to use an indirect branch instruction in a
99 // long-branch thunk, it's also responsible for adding a 'landing pad' with
100 // a BTI, and pointing the indirect branch at that. For non-ELF targets we
101 // can't rely on that, so we assume that `CouldCall` is _always_ true due
102 // to the risk of long-branch thunks at link time.
103 if (&MBB == &*MF.begin() &&
104 (!MF.getSubtarget<AArch64Subtarget>().isTargetELF() ||
105 (F.hasAddressTaken() || !F.hasLocalLinkage())))
106 CouldCall = true;
107
108 // If the block itself is address-taken, it could be indirectly branched
109 // to, but not called.
111 JumpTableTargets.count(&MBB))
112 CouldJump = true;
113
114 if (MBB.isEHPad()) {
115 if (HasWinCFI && (MBB.isEHFuncletEntry() || MBB.isCleanupFuncletEntry()))
116 CouldCall = true;
117 else
118 CouldJump = true;
119 }
120 if (CouldCall || CouldJump) {
121 addBTI(MBB, CouldCall, CouldJump, HasWinCFI);
122 MadeChange = true;
123 }
124 }
125
126 return MadeChange;
127}
128
129void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,
130 bool CouldJump, bool HasWinCFI) {
131 LLVM_DEBUG(dbgs() << "Adding BTI " << (CouldJump ? "j" : "")
132 << (CouldCall ? "c" : "") << " to " << MBB.getName()
133 << "\n");
134
135 const AArch64InstrInfo *TII = static_cast<const AArch64InstrInfo *>(
136 MBB.getParent()->getSubtarget().getInstrInfo());
137
138 unsigned HintNum = 32;
139 if (CouldCall)
140 HintNum |= 2;
141 if (CouldJump)
142 HintNum |= 4;
143 assert(HintNum != 32 && "No target kinds!");
144
145 auto MBBI = MBB.begin();
146
147 // If the block starts with EH_LABEL(s), skip them first.
148 while (MBBI != MBB.end() && MBBI->isEHLabel()) {
149 ++MBBI;
150 }
151
152 // Skip meta/CFI/etc. (and EMITBKEY) to reach the first executable insn.
153 for (; MBBI != MBB.end() &&
154 (MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY);
155 ++MBBI)
156 ;
157
158 // SCTLR_EL1.BT[01] is set to 0 by default which means
159 // PACI[AB]SP are implicitly BTI C so no BTI C instruction is needed there.
160 if (MBBI != MBB.end() && ((HintNum & BTIMask) == BTIC) &&
161 (MBBI->getOpcode() == AArch64::PACIASP ||
162 MBBI->getOpcode() == AArch64::PACIBSP))
163 return;
164
165 // Insert BTI exactly at the first executable instruction.
167 MachineInstr *BTI = BuildMI(MBB, MBBI, DL, TII->get(AArch64::HINT))
168 .addImm(HintNum)
169 .getInstr();
170
171 // WinEH: put .seh_nop after BTI when the first real insn is FrameSetup.
172 if (HasWinCFI && MBBI != MBB.end() &&
173 MBBI->getFlag(MachineInstr::FrameSetup)) {
174 auto AfterBTI = std::next(MachineBasicBlock::iterator(BTI));
175 BuildMI(MBB, AfterBTI, DL, TII->get(AArch64::SEH_Nop));
176 }
177}
#define AARCH64_BRANCH_TARGETS_NAME
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
MachineBasicBlock MachineBasicBlock::iterator MBBI
const HexagonInstrInfo * TII
#define F(x, y, z)
Definition MD5.cpp:55
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
#define LLVM_DEBUG(...)
Definition Debug.h:114
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.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
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.
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.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
void insert_range(Range &&R)
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.
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()