xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/ARM/ARMBranchTargets.cpp (revision 4824e7fd18a1223177218d4aec1b3c6c5c4a444e)
1*4824e7fdSDimitry Andric //===-- ARMBranchTargets.cpp -- Harden code using v8.1-M BTI extension -----==//
2*4824e7fdSDimitry Andric //
3*4824e7fdSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*4824e7fdSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*4824e7fdSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*4824e7fdSDimitry Andric //
7*4824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
8*4824e7fdSDimitry Andric //
9*4824e7fdSDimitry Andric // This pass inserts BTI instructions at the start of every function and basic
10*4824e7fdSDimitry Andric // block which could be indirectly called. The hardware will (when enabled)
11*4824e7fdSDimitry Andric // trap when an indirect branch or call instruction targets an instruction
12*4824e7fdSDimitry Andric // which is not a valid BTI instruction. This is intended to guard against
13*4824e7fdSDimitry Andric // control-flow hijacking attacks.
14*4824e7fdSDimitry Andric //
15*4824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
16*4824e7fdSDimitry Andric 
17*4824e7fdSDimitry Andric #include "ARM.h"
18*4824e7fdSDimitry Andric #include "ARMInstrInfo.h"
19*4824e7fdSDimitry Andric #include "ARMMachineFunctionInfo.h"
20*4824e7fdSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
21*4824e7fdSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
22*4824e7fdSDimitry Andric #include "llvm/CodeGen/MachineJumpTableInfo.h"
23*4824e7fdSDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
24*4824e7fdSDimitry Andric #include "llvm/Support/Debug.h"
25*4824e7fdSDimitry Andric 
26*4824e7fdSDimitry Andric using namespace llvm;
27*4824e7fdSDimitry Andric 
28*4824e7fdSDimitry Andric #define DEBUG_TYPE "arm-branch-targets"
29*4824e7fdSDimitry Andric #define ARM_BRANCH_TARGETS_NAME "ARM Branch Targets"
30*4824e7fdSDimitry Andric 
31*4824e7fdSDimitry Andric namespace {
32*4824e7fdSDimitry Andric class ARMBranchTargets : public MachineFunctionPass {
33*4824e7fdSDimitry Andric public:
34*4824e7fdSDimitry Andric   static char ID;
35*4824e7fdSDimitry Andric   ARMBranchTargets() : MachineFunctionPass(ID) {}
36*4824e7fdSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override;
37*4824e7fdSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
38*4824e7fdSDimitry Andric   StringRef getPassName() const override { return ARM_BRANCH_TARGETS_NAME; }
39*4824e7fdSDimitry Andric 
40*4824e7fdSDimitry Andric private:
41*4824e7fdSDimitry Andric   void addBTI(const ARMInstrInfo &TII, MachineBasicBlock &MBB, bool IsFirstBB);
42*4824e7fdSDimitry Andric };
43*4824e7fdSDimitry Andric } // end anonymous namespace
44*4824e7fdSDimitry Andric 
45*4824e7fdSDimitry Andric char ARMBranchTargets::ID = 0;
46*4824e7fdSDimitry Andric 
47*4824e7fdSDimitry Andric INITIALIZE_PASS(ARMBranchTargets, "arm-branch-targets", ARM_BRANCH_TARGETS_NAME,
48*4824e7fdSDimitry Andric                 false, false)
49*4824e7fdSDimitry Andric 
50*4824e7fdSDimitry Andric void ARMBranchTargets::getAnalysisUsage(AnalysisUsage &AU) const {
51*4824e7fdSDimitry Andric   AU.setPreservesCFG();
52*4824e7fdSDimitry Andric   MachineFunctionPass::getAnalysisUsage(AU);
53*4824e7fdSDimitry Andric }
54*4824e7fdSDimitry Andric 
55*4824e7fdSDimitry Andric FunctionPass *llvm::createARMBranchTargetsPass() {
56*4824e7fdSDimitry Andric   return new ARMBranchTargets();
57*4824e7fdSDimitry Andric }
58*4824e7fdSDimitry Andric 
59*4824e7fdSDimitry Andric bool ARMBranchTargets::runOnMachineFunction(MachineFunction &MF) {
60*4824e7fdSDimitry Andric   if (!MF.getInfo<ARMFunctionInfo>()->branchTargetEnforcement())
61*4824e7fdSDimitry Andric     return false;
62*4824e7fdSDimitry Andric 
63*4824e7fdSDimitry Andric   LLVM_DEBUG(dbgs() << "********** ARM Branch Targets  **********\n"
64*4824e7fdSDimitry Andric                     << "********** Function: " << MF.getName() << '\n');
65*4824e7fdSDimitry Andric   const ARMInstrInfo &TII =
66*4824e7fdSDimitry Andric       *static_cast<const ARMInstrInfo *>(MF.getSubtarget().getInstrInfo());
67*4824e7fdSDimitry Andric 
68*4824e7fdSDimitry Andric   // LLVM does not consider basic blocks which are the targets of jump tables
69*4824e7fdSDimitry Andric   // to be address-taken (the address can't escape anywhere else), but they are
70*4824e7fdSDimitry Andric   // used for indirect branches, so need BTI instructions.
71*4824e7fdSDimitry Andric   SmallPtrSet<const MachineBasicBlock *, 8> JumpTableTargets;
72*4824e7fdSDimitry Andric   if (const MachineJumpTableInfo *JTI = MF.getJumpTableInfo())
73*4824e7fdSDimitry Andric     for (const MachineJumpTableEntry &JTE : JTI->getJumpTables())
74*4824e7fdSDimitry Andric       for (const MachineBasicBlock *MBB : JTE.MBBs)
75*4824e7fdSDimitry Andric         JumpTableTargets.insert(MBB);
76*4824e7fdSDimitry Andric 
77*4824e7fdSDimitry Andric   bool MadeChange = false;
78*4824e7fdSDimitry Andric   for (MachineBasicBlock &MBB : MF) {
79*4824e7fdSDimitry Andric     bool NeedBTI = false;
80*4824e7fdSDimitry Andric     bool IsFirstBB = &MBB == &MF.front();
81*4824e7fdSDimitry Andric 
82*4824e7fdSDimitry Andric     // Every function can potentially be called indirectly (even if it has
83*4824e7fdSDimitry Andric     // static linkage, due to linker-generated veneers).
84*4824e7fdSDimitry Andric     if (IsFirstBB)
85*4824e7fdSDimitry Andric       NeedBTI = true;
86*4824e7fdSDimitry Andric 
87*4824e7fdSDimitry Andric     // If the block itself is address-taken, or is an exception landing pad, it
88*4824e7fdSDimitry Andric     // could be indirectly branched to.
89*4824e7fdSDimitry Andric     if (MBB.hasAddressTaken() || MBB.isEHPad() || JumpTableTargets.count(&MBB))
90*4824e7fdSDimitry Andric       NeedBTI = true;
91*4824e7fdSDimitry Andric 
92*4824e7fdSDimitry Andric     if (NeedBTI) {
93*4824e7fdSDimitry Andric       addBTI(TII, MBB, IsFirstBB);
94*4824e7fdSDimitry Andric       MadeChange = true;
95*4824e7fdSDimitry Andric     }
96*4824e7fdSDimitry Andric   }
97*4824e7fdSDimitry Andric 
98*4824e7fdSDimitry Andric   return MadeChange;
99*4824e7fdSDimitry Andric }
100*4824e7fdSDimitry Andric 
101*4824e7fdSDimitry Andric /// Insert a BTI/PACBTI instruction into a given basic block \c MBB. If
102*4824e7fdSDimitry Andric /// \c IsFirstBB is true (meaning that this is the first BB in a function) try
103*4824e7fdSDimitry Andric /// to find a PAC instruction and replace it with PACBTI. Otherwise just insert
104*4824e7fdSDimitry Andric /// a BTI instruction.
105*4824e7fdSDimitry Andric /// The point of insertion is in the beginning of the BB, immediately after meta
106*4824e7fdSDimitry Andric /// instructions (such labels in exception handling landing pads).
107*4824e7fdSDimitry Andric void ARMBranchTargets::addBTI(const ARMInstrInfo &TII, MachineBasicBlock &MBB,
108*4824e7fdSDimitry Andric                               bool IsFirstBB) {
109*4824e7fdSDimitry Andric   // Which instruction to insert: BTI or PACBTI
110*4824e7fdSDimitry Andric   unsigned OpCode = ARM::t2BTI;
111*4824e7fdSDimitry Andric 
112*4824e7fdSDimitry Andric   // Skip meta instructions, including EH labels
113*4824e7fdSDimitry Andric   auto MBBI = llvm::find_if_not(MBB.instrs(), [](const MachineInstr &MI) {
114*4824e7fdSDimitry Andric     return MI.isMetaInstruction();
115*4824e7fdSDimitry Andric   });
116*4824e7fdSDimitry Andric 
117*4824e7fdSDimitry Andric   // If this is the first BB in a function, check if it starts with a PAC
118*4824e7fdSDimitry Andric   // instruction and in that case remove the PAC instruction.
119*4824e7fdSDimitry Andric   if (IsFirstBB) {
120*4824e7fdSDimitry Andric     if (MBBI != MBB.instr_end() && MBBI->getOpcode() == ARM::t2PAC) {
121*4824e7fdSDimitry Andric       LLVM_DEBUG(dbgs() << "Removing a 'PAC' instr from BB '" << MBB.getName()
122*4824e7fdSDimitry Andric                         << "' to replace with PACBTI\n");
123*4824e7fdSDimitry Andric       OpCode = ARM::t2PACBTI;
124*4824e7fdSDimitry Andric       auto NextMBBI = std::next(MBBI);
125*4824e7fdSDimitry Andric       MBBI->eraseFromParent();
126*4824e7fdSDimitry Andric       MBBI = NextMBBI;
127*4824e7fdSDimitry Andric     }
128*4824e7fdSDimitry Andric   }
129*4824e7fdSDimitry Andric 
130*4824e7fdSDimitry Andric   LLVM_DEBUG(dbgs() << "Inserting a '"
131*4824e7fdSDimitry Andric                     << (OpCode == ARM::t2BTI ? "BTI" : "PACBTI")
132*4824e7fdSDimitry Andric                     << "' instr into BB '" << MBB.getName() << "'\n");
133*4824e7fdSDimitry Andric   // Finally, insert a new instruction (either PAC or PACBTI)
134*4824e7fdSDimitry Andric   BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII.get(OpCode));
135*4824e7fdSDimitry Andric }
136