xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
15f757f3fSDimitry Andric //===-- AArch64PointerAuth.cpp -- Harden code using PAuth ------------------==//
25f757f3fSDimitry Andric //
35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f757f3fSDimitry Andric //
75f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
85f757f3fSDimitry Andric 
95f757f3fSDimitry Andric #include "AArch64PointerAuth.h"
105f757f3fSDimitry Andric 
115f757f3fSDimitry Andric #include "AArch64.h"
125f757f3fSDimitry Andric #include "AArch64InstrInfo.h"
135f757f3fSDimitry Andric #include "AArch64MachineFunctionInfo.h"
145f757f3fSDimitry Andric #include "AArch64Subtarget.h"
155f757f3fSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
165f757f3fSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
175f757f3fSDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
185f757f3fSDimitry Andric 
195f757f3fSDimitry Andric using namespace llvm;
205f757f3fSDimitry Andric using namespace llvm::AArch64PAuth;
215f757f3fSDimitry Andric 
225f757f3fSDimitry Andric #define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication"
235f757f3fSDimitry Andric 
245f757f3fSDimitry Andric namespace {
255f757f3fSDimitry Andric 
265f757f3fSDimitry Andric class AArch64PointerAuth : public MachineFunctionPass {
275f757f3fSDimitry Andric public:
285f757f3fSDimitry Andric   static char ID;
295f757f3fSDimitry Andric 
305f757f3fSDimitry Andric   AArch64PointerAuth() : MachineFunctionPass(ID) {}
315f757f3fSDimitry Andric 
325f757f3fSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
335f757f3fSDimitry Andric 
345f757f3fSDimitry Andric   StringRef getPassName() const override { return AARCH64_POINTER_AUTH_NAME; }
355f757f3fSDimitry Andric 
365f757f3fSDimitry Andric private:
375f757f3fSDimitry Andric   /// An immediate operand passed to BRK instruction, if it is ever emitted.
385f757f3fSDimitry Andric   const unsigned BrkOperand = 0xc471;
395f757f3fSDimitry Andric 
405f757f3fSDimitry Andric   const AArch64Subtarget *Subtarget = nullptr;
415f757f3fSDimitry Andric   const AArch64InstrInfo *TII = nullptr;
425f757f3fSDimitry Andric   const AArch64RegisterInfo *TRI = nullptr;
435f757f3fSDimitry Andric 
445f757f3fSDimitry Andric   void signLR(MachineFunction &MF, MachineBasicBlock::iterator MBBI) const;
455f757f3fSDimitry Andric 
465f757f3fSDimitry Andric   void authenticateLR(MachineFunction &MF,
475f757f3fSDimitry Andric                       MachineBasicBlock::iterator MBBI) const;
485f757f3fSDimitry Andric 
495f757f3fSDimitry Andric   bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const;
505f757f3fSDimitry Andric };
515f757f3fSDimitry Andric 
525f757f3fSDimitry Andric } // end anonymous namespace
535f757f3fSDimitry Andric 
545f757f3fSDimitry Andric INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth",
555f757f3fSDimitry Andric                 AARCH64_POINTER_AUTH_NAME, false, false)
565f757f3fSDimitry Andric 
575f757f3fSDimitry Andric FunctionPass *llvm::createAArch64PointerAuthPass() {
585f757f3fSDimitry Andric   return new AArch64PointerAuth();
595f757f3fSDimitry Andric }
605f757f3fSDimitry Andric 
615f757f3fSDimitry Andric char AArch64PointerAuth::ID = 0;
625f757f3fSDimitry Andric 
63*cb14a3feSDimitry Andric // Where PAuthLR support is not known at compile time, it is supported using
64*cb14a3feSDimitry Andric // PACM. PACM is in the hint space so has no effect when PAuthLR is not
65*cb14a3feSDimitry Andric // supported by the hardware, but will alter the behaviour of PACI*SP, AUTI*SP
66*cb14a3feSDimitry Andric // and RETAA/RETAB if the hardware supports PAuthLR.
67*cb14a3feSDimitry Andric static void BuildPACM(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB,
68*cb14a3feSDimitry Andric                       MachineBasicBlock::iterator MBBI, DebugLoc DL,
69*cb14a3feSDimitry Andric                       MachineInstr::MIFlag Flags, MCSymbol *PACSym = nullptr) {
70*cb14a3feSDimitry Andric   const TargetInstrInfo *TII = Subtarget.getInstrInfo();
71*cb14a3feSDimitry Andric   auto &MFnI = *MBB.getParent()->getInfo<AArch64FunctionInfo>();
72*cb14a3feSDimitry Andric 
73*cb14a3feSDimitry Andric   // ADR X16,<address_of_PACIASP>
74*cb14a3feSDimitry Andric   if (PACSym) {
75*cb14a3feSDimitry Andric     assert(Flags == MachineInstr::FrameDestroy);
76*cb14a3feSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADR))
77*cb14a3feSDimitry Andric         .addReg(AArch64::X16, RegState::Define)
78*cb14a3feSDimitry Andric         .addSym(PACSym);
79*cb14a3feSDimitry Andric   }
80*cb14a3feSDimitry Andric 
81*cb14a3feSDimitry Andric   // Only emit PACM if -mbranch-protection has +pc and the target does not
82*cb14a3feSDimitry Andric   // have feature +pauth-lr.
83*cb14a3feSDimitry Andric   if (MFnI.branchProtectionPAuthLR() && !Subtarget.hasPAuthLR())
84*cb14a3feSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACM)).setMIFlag(Flags);
85*cb14a3feSDimitry Andric }
86*cb14a3feSDimitry Andric 
875f757f3fSDimitry Andric void AArch64PointerAuth::signLR(MachineFunction &MF,
885f757f3fSDimitry Andric                                 MachineBasicBlock::iterator MBBI) const {
89*cb14a3feSDimitry Andric   auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
90*cb14a3feSDimitry Andric   bool UseBKey = MFnI.shouldSignWithBKey();
91*cb14a3feSDimitry Andric   bool EmitCFI = MFnI.needsDwarfUnwindInfo(MF);
925f757f3fSDimitry Andric   bool NeedsWinCFI = MF.hasWinCFI();
935f757f3fSDimitry Andric 
945f757f3fSDimitry Andric   MachineBasicBlock &MBB = *MBBI->getParent();
955f757f3fSDimitry Andric 
965f757f3fSDimitry Andric   // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
975f757f3fSDimitry Andric   DebugLoc DL;
985f757f3fSDimitry Andric 
995f757f3fSDimitry Andric   if (UseBKey) {
1005f757f3fSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
1015f757f3fSDimitry Andric         .setMIFlag(MachineInstr::FrameSetup);
1025f757f3fSDimitry Andric   }
1035f757f3fSDimitry Andric 
104*cb14a3feSDimitry Andric   // PAuthLR authentication instructions need to know the value of PC at the
105*cb14a3feSDimitry Andric   // point of signing (PACI*).
106*cb14a3feSDimitry Andric   if (MFnI.branchProtectionPAuthLR()) {
107*cb14a3feSDimitry Andric     MCSymbol *PACSym = MF.getMMI().getContext().createTempSymbol();
108*cb14a3feSDimitry Andric     MFnI.setSigningInstrLabel(PACSym);
109*cb14a3feSDimitry Andric   }
110*cb14a3feSDimitry Andric 
1115f757f3fSDimitry Andric   // No SEH opcode for this one; it doesn't materialize into an
1125f757f3fSDimitry Andric   // instruction on Windows.
113*cb14a3feSDimitry Andric   if (MFnI.branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
1145f757f3fSDimitry Andric     BuildMI(MBB, MBBI, DL,
115*cb14a3feSDimitry Andric             TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC
116*cb14a3feSDimitry Andric                                                : AArch64::PACIASPPC))
117*cb14a3feSDimitry Andric         .setMIFlag(MachineInstr::FrameSetup)
118*cb14a3feSDimitry Andric         ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
119*cb14a3feSDimitry Andric   } else {
120*cb14a3feSDimitry Andric     BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup);
121*cb14a3feSDimitry Andric     BuildMI(MBB, MBBI, DL,
122*cb14a3feSDimitry Andric             TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP
123*cb14a3feSDimitry Andric                                                : AArch64::PACIASP))
124*cb14a3feSDimitry Andric         .setMIFlag(MachineInstr::FrameSetup)
125*cb14a3feSDimitry Andric         ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
126*cb14a3feSDimitry Andric   }
1275f757f3fSDimitry Andric 
1285f757f3fSDimitry Andric   if (EmitCFI) {
1295f757f3fSDimitry Andric     unsigned CFIIndex =
1305f757f3fSDimitry Andric         MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
1315f757f3fSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
1325f757f3fSDimitry Andric         .addCFIIndex(CFIIndex)
1335f757f3fSDimitry Andric         .setMIFlags(MachineInstr::FrameSetup);
1345f757f3fSDimitry Andric   } else if (NeedsWinCFI) {
1355f757f3fSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
1365f757f3fSDimitry Andric         .setMIFlag(MachineInstr::FrameSetup);
1375f757f3fSDimitry Andric   }
1385f757f3fSDimitry Andric }
1395f757f3fSDimitry Andric 
1405f757f3fSDimitry Andric void AArch64PointerAuth::authenticateLR(
1415f757f3fSDimitry Andric     MachineFunction &MF, MachineBasicBlock::iterator MBBI) const {
1425f757f3fSDimitry Andric   const AArch64FunctionInfo *MFnI = MF.getInfo<AArch64FunctionInfo>();
1435f757f3fSDimitry Andric   bool UseBKey = MFnI->shouldSignWithBKey();
1445f757f3fSDimitry Andric   bool EmitAsyncCFI = MFnI->needsAsyncDwarfUnwindInfo(MF);
1455f757f3fSDimitry Andric   bool NeedsWinCFI = MF.hasWinCFI();
1465f757f3fSDimitry Andric 
1475f757f3fSDimitry Andric   MachineBasicBlock &MBB = *MBBI->getParent();
1485f757f3fSDimitry Andric   DebugLoc DL = MBBI->getDebugLoc();
1495f757f3fSDimitry Andric   // MBBI points to a PAUTH_EPILOGUE instruction to be replaced and
1505f757f3fSDimitry Andric   // TI points to a terminator instruction that may or may not be combined.
1515f757f3fSDimitry Andric   // Note that inserting new instructions "before MBBI" and "before TI" is
1525f757f3fSDimitry Andric   // not the same because if ShadowCallStack is enabled, its instructions
1535f757f3fSDimitry Andric   // are placed between MBBI and TI.
1545f757f3fSDimitry Andric   MachineBasicBlock::iterator TI = MBB.getFirstInstrTerminator();
1555f757f3fSDimitry Andric 
1565f757f3fSDimitry Andric   // The AUTIASP instruction assembles to a hint instruction before v8.3a so
1575f757f3fSDimitry Andric   // this instruction can safely used for any v8a architecture.
1585f757f3fSDimitry Andric   // From v8.3a onwards there are optimised authenticate LR and return
1595f757f3fSDimitry Andric   // instructions, namely RETA{A,B}, that can be used instead. In this case the
1605f757f3fSDimitry Andric   // DW_CFA_AARCH64_negate_ra_state can't be emitted.
1615f757f3fSDimitry Andric   bool TerminatorIsCombinable =
1625f757f3fSDimitry Andric       TI != MBB.end() && TI->getOpcode() == AArch64::RET;
163*cb14a3feSDimitry Andric   MCSymbol *PACSym = MFnI->getSigningInstrLabel();
164*cb14a3feSDimitry Andric 
1655f757f3fSDimitry Andric   if (Subtarget->hasPAuth() && TerminatorIsCombinable && !NeedsWinCFI &&
1665f757f3fSDimitry Andric       !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) {
167*cb14a3feSDimitry Andric     if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
168*cb14a3feSDimitry Andric       assert(PACSym && "No PAC instruction to refer to");
169*cb14a3feSDimitry Andric       BuildMI(MBB, TI, DL,
170*cb14a3feSDimitry Andric               TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi))
171*cb14a3feSDimitry Andric           .addSym(PACSym)
172*cb14a3feSDimitry Andric           .copyImplicitOps(*MBBI)
173*cb14a3feSDimitry Andric           .setMIFlag(MachineInstr::FrameDestroy);
174*cb14a3feSDimitry Andric     } else {
175*cb14a3feSDimitry Andric       BuildPACM(*Subtarget, MBB, TI, DL, MachineInstr::FrameDestroy, PACSym);
176*cb14a3feSDimitry Andric       BuildMI(MBB, TI, DL, TII->get(UseBKey ? AArch64::RETAB : AArch64::RETAA))
177*cb14a3feSDimitry Andric           .copyImplicitOps(*MBBI)
178*cb14a3feSDimitry Andric           .setMIFlag(MachineInstr::FrameDestroy);
179*cb14a3feSDimitry Andric     }
1805f757f3fSDimitry Andric     MBB.erase(TI);
1815f757f3fSDimitry Andric   } else {
182*cb14a3feSDimitry Andric     if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
183*cb14a3feSDimitry Andric       assert(PACSym && "No PAC instruction to refer to");
184*cb14a3feSDimitry Andric       BuildMI(MBB, MBBI, DL,
185*cb14a3feSDimitry Andric               TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi))
186*cb14a3feSDimitry Andric           .addSym(PACSym)
1875f757f3fSDimitry Andric           .setMIFlag(MachineInstr::FrameDestroy);
188*cb14a3feSDimitry Andric     } else {
189*cb14a3feSDimitry Andric       BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym);
190*cb14a3feSDimitry Andric       BuildMI(MBB, MBBI, DL,
191*cb14a3feSDimitry Andric               TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP))
192*cb14a3feSDimitry Andric           .setMIFlag(MachineInstr::FrameDestroy);
193*cb14a3feSDimitry Andric     }
1945f757f3fSDimitry Andric 
1955f757f3fSDimitry Andric     if (EmitAsyncCFI) {
1965f757f3fSDimitry Andric       unsigned CFIIndex =
1975f757f3fSDimitry Andric           MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
1985f757f3fSDimitry Andric       BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
1995f757f3fSDimitry Andric           .addCFIIndex(CFIIndex)
2005f757f3fSDimitry Andric           .setMIFlags(MachineInstr::FrameDestroy);
2015f757f3fSDimitry Andric     }
2025f757f3fSDimitry Andric     if (NeedsWinCFI) {
2035f757f3fSDimitry Andric       BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
2045f757f3fSDimitry Andric           .setMIFlag(MachineInstr::FrameDestroy);
2055f757f3fSDimitry Andric     }
2065f757f3fSDimitry Andric   }
2075f757f3fSDimitry Andric }
2085f757f3fSDimitry Andric 
2095f757f3fSDimitry Andric namespace {
2105f757f3fSDimitry Andric 
2115f757f3fSDimitry Andric // Mark dummy LDR instruction as volatile to prevent removing it as dead code.
2125f757f3fSDimitry Andric MachineMemOperand *createCheckMemOperand(MachineFunction &MF,
2135f757f3fSDimitry Andric                                          const AArch64Subtarget &Subtarget) {
2145f757f3fSDimitry Andric   MachinePointerInfo PointerInfo(Subtarget.getAddressCheckPSV());
2155f757f3fSDimitry Andric   auto MOVolatileLoad =
2165f757f3fSDimitry Andric       MachineMemOperand::MOLoad | MachineMemOperand::MOVolatile;
2175f757f3fSDimitry Andric 
2185f757f3fSDimitry Andric   return MF.getMachineMemOperand(PointerInfo, MOVolatileLoad, 4, Align(4));
2195f757f3fSDimitry Andric }
2205f757f3fSDimitry Andric 
2215f757f3fSDimitry Andric } // namespace
2225f757f3fSDimitry Andric 
2235f757f3fSDimitry Andric MachineBasicBlock &llvm::AArch64PAuth::checkAuthenticatedRegister(
2245f757f3fSDimitry Andric     MachineBasicBlock::iterator MBBI, AuthCheckMethod Method,
2255f757f3fSDimitry Andric     Register AuthenticatedReg, Register TmpReg, bool UseIKey, unsigned BrkImm) {
2265f757f3fSDimitry Andric 
2275f757f3fSDimitry Andric   MachineBasicBlock &MBB = *MBBI->getParent();
2285f757f3fSDimitry Andric   MachineFunction &MF = *MBB.getParent();
2295f757f3fSDimitry Andric   const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
2305f757f3fSDimitry Andric   const AArch64InstrInfo *TII = Subtarget.getInstrInfo();
2315f757f3fSDimitry Andric   DebugLoc DL = MBBI->getDebugLoc();
2325f757f3fSDimitry Andric 
2335f757f3fSDimitry Andric   // First, handle the methods not requiring creating extra MBBs.
2345f757f3fSDimitry Andric   switch (Method) {
2355f757f3fSDimitry Andric   default:
2365f757f3fSDimitry Andric     break;
2375f757f3fSDimitry Andric   case AuthCheckMethod::None:
2385f757f3fSDimitry Andric     return MBB;
2395f757f3fSDimitry Andric   case AuthCheckMethod::DummyLoad:
2405f757f3fSDimitry Andric     BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRWui), getWRegFromXReg(TmpReg))
2415f757f3fSDimitry Andric         .addReg(AArch64::LR)
2425f757f3fSDimitry Andric         .addImm(0)
2435f757f3fSDimitry Andric         .addMemOperand(createCheckMemOperand(MF, Subtarget));
2445f757f3fSDimitry Andric     return MBB;
2455f757f3fSDimitry Andric   }
2465f757f3fSDimitry Andric 
2475f757f3fSDimitry Andric   // Control flow has to be changed, so arrange new MBBs.
2485f757f3fSDimitry Andric 
2495f757f3fSDimitry Andric   // At now, at least an AUT* instruction is expected before MBBI
2505f757f3fSDimitry Andric   assert(MBBI != MBB.begin() &&
2515f757f3fSDimitry Andric          "Cannot insert the check at the very beginning of MBB");
2525f757f3fSDimitry Andric   // The block to insert check into.
2535f757f3fSDimitry Andric   MachineBasicBlock *CheckBlock = &MBB;
2545f757f3fSDimitry Andric   // The remaining part of the original MBB that is executed on success.
2555f757f3fSDimitry Andric   MachineBasicBlock *SuccessBlock = MBB.splitAt(*std::prev(MBBI));
2565f757f3fSDimitry Andric 
2575f757f3fSDimitry Andric   // The block that explicitly generates a break-point exception on failure.
2585f757f3fSDimitry Andric   MachineBasicBlock *BreakBlock =
2595f757f3fSDimitry Andric       MF.CreateMachineBasicBlock(MBB.getBasicBlock());
2605f757f3fSDimitry Andric   MF.push_back(BreakBlock);
2615f757f3fSDimitry Andric   MBB.splitSuccessor(SuccessBlock, BreakBlock);
2625f757f3fSDimitry Andric 
2635f757f3fSDimitry Andric   assert(CheckBlock->getFallThrough() == SuccessBlock);
2645f757f3fSDimitry Andric   BuildMI(BreakBlock, DL, TII->get(AArch64::BRK)).addImm(BrkImm);
2655f757f3fSDimitry Andric 
2665f757f3fSDimitry Andric   switch (Method) {
2675f757f3fSDimitry Andric   case AuthCheckMethod::None:
2685f757f3fSDimitry Andric   case AuthCheckMethod::DummyLoad:
2695f757f3fSDimitry Andric     llvm_unreachable("Should be handled above");
2705f757f3fSDimitry Andric   case AuthCheckMethod::HighBitsNoTBI:
2715f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::EORXrs), TmpReg)
2725f757f3fSDimitry Andric         .addReg(AuthenticatedReg)
2735f757f3fSDimitry Andric         .addReg(AuthenticatedReg)
2745f757f3fSDimitry Andric         .addImm(1);
2755f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::TBNZX))
2765f757f3fSDimitry Andric         .addReg(TmpReg)
2775f757f3fSDimitry Andric         .addImm(62)
2785f757f3fSDimitry Andric         .addMBB(BreakBlock);
2795f757f3fSDimitry Andric     return *SuccessBlock;
2805f757f3fSDimitry Andric   case AuthCheckMethod::XPACHint:
2815f757f3fSDimitry Andric     assert(AuthenticatedReg == AArch64::LR &&
2825f757f3fSDimitry Andric            "XPACHint mode is only compatible with checking the LR register");
2835f757f3fSDimitry Andric     assert(UseIKey && "XPACHint mode is only compatible with I-keys");
2845f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::ORRXrs), TmpReg)
2855f757f3fSDimitry Andric         .addReg(AArch64::XZR)
2865f757f3fSDimitry Andric         .addReg(AArch64::LR)
2875f757f3fSDimitry Andric         .addImm(0);
2885f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::XPACLRI));
2895f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR)
2905f757f3fSDimitry Andric         .addReg(TmpReg)
2915f757f3fSDimitry Andric         .addReg(AArch64::LR)
2925f757f3fSDimitry Andric         .addImm(0);
2935f757f3fSDimitry Andric     BuildMI(CheckBlock, DL, TII->get(AArch64::Bcc))
2945f757f3fSDimitry Andric         .addImm(AArch64CC::NE)
2955f757f3fSDimitry Andric         .addMBB(BreakBlock);
2965f757f3fSDimitry Andric     return *SuccessBlock;
2975f757f3fSDimitry Andric   }
2985f757f3fSDimitry Andric   llvm_unreachable("Unknown AuthCheckMethod enum");
2995f757f3fSDimitry Andric }
3005f757f3fSDimitry Andric 
3015f757f3fSDimitry Andric unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) {
3025f757f3fSDimitry Andric   switch (Method) {
3035f757f3fSDimitry Andric   case AuthCheckMethod::None:
3045f757f3fSDimitry Andric     return 0;
3055f757f3fSDimitry Andric   case AuthCheckMethod::DummyLoad:
3065f757f3fSDimitry Andric     return 4;
3075f757f3fSDimitry Andric   case AuthCheckMethod::HighBitsNoTBI:
3085f757f3fSDimitry Andric     return 12;
3095f757f3fSDimitry Andric   case AuthCheckMethod::XPACHint:
3105f757f3fSDimitry Andric     return 20;
3115f757f3fSDimitry Andric   }
3125f757f3fSDimitry Andric   llvm_unreachable("Unknown AuthCheckMethod enum");
3135f757f3fSDimitry Andric }
3145f757f3fSDimitry Andric 
3155f757f3fSDimitry Andric bool AArch64PointerAuth::checkAuthenticatedLR(
3165f757f3fSDimitry Andric     MachineBasicBlock::iterator TI) const {
3175f757f3fSDimitry Andric   AuthCheckMethod Method = Subtarget->getAuthenticatedLRCheckMethod();
3185f757f3fSDimitry Andric 
3195f757f3fSDimitry Andric   if (Method == AuthCheckMethod::None)
3205f757f3fSDimitry Andric     return false;
3215f757f3fSDimitry Andric 
3225f757f3fSDimitry Andric   // FIXME If FEAT_FPAC is implemented by the CPU, this check can be skipped.
3235f757f3fSDimitry Andric 
3245f757f3fSDimitry Andric   assert(!TI->getMF()->hasWinCFI() && "WinCFI is not yet supported");
3255f757f3fSDimitry Andric 
3265f757f3fSDimitry Andric   // The following code may create a signing oracle:
3275f757f3fSDimitry Andric   //
3285f757f3fSDimitry Andric   //   <authenticate LR>
3295f757f3fSDimitry Andric   //   TCRETURN          ; the callee may sign and spill the LR in its prologue
3305f757f3fSDimitry Andric   //
3315f757f3fSDimitry Andric   // To avoid generating a signing oracle, check the authenticated value
3325f757f3fSDimitry Andric   // before possibly re-signing it in the callee, as follows:
3335f757f3fSDimitry Andric   //
3345f757f3fSDimitry Andric   //   <authenticate LR>
3355f757f3fSDimitry Andric   //   <check if LR contains a valid address>
3365f757f3fSDimitry Andric   //   b.<cond> break_block
3375f757f3fSDimitry Andric   // ret_block:
3385f757f3fSDimitry Andric   //   TCRETURN
3395f757f3fSDimitry Andric   // break_block:
3405f757f3fSDimitry Andric   //   brk <BrkOperand>
3415f757f3fSDimitry Andric   //
3425f757f3fSDimitry Andric   // or just
3435f757f3fSDimitry Andric   //
3445f757f3fSDimitry Andric   //   <authenticate LR>
3455f757f3fSDimitry Andric   //   ldr tmp, [lr]
3465f757f3fSDimitry Andric   //   TCRETURN
3475f757f3fSDimitry Andric 
3485f757f3fSDimitry Andric   // TmpReg is chosen assuming X16 and X17 are dead after TI.
3495f757f3fSDimitry Andric   assert(AArch64InstrInfo::isTailCallReturnInst(*TI) &&
3505f757f3fSDimitry Andric          "Tail call is expected");
3515f757f3fSDimitry Andric   Register TmpReg =
3525f757f3fSDimitry Andric       TI->readsRegister(AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16;
3535f757f3fSDimitry Andric   assert(!TI->readsRegister(TmpReg, TRI) &&
3545f757f3fSDimitry Andric          "More than a single register is used by TCRETURN");
3555f757f3fSDimitry Andric 
3565f757f3fSDimitry Andric   checkAuthenticatedRegister(TI, Method, AArch64::LR, TmpReg, /*UseIKey=*/true,
3575f757f3fSDimitry Andric                              BrkOperand);
3585f757f3fSDimitry Andric 
3595f757f3fSDimitry Andric   return true;
3605f757f3fSDimitry Andric }
3615f757f3fSDimitry Andric 
3625f757f3fSDimitry Andric bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) {
3635f757f3fSDimitry Andric   const auto *MFnI = MF.getInfo<AArch64FunctionInfo>();
3645f757f3fSDimitry Andric 
3655f757f3fSDimitry Andric   Subtarget = &MF.getSubtarget<AArch64Subtarget>();
3665f757f3fSDimitry Andric   TII = Subtarget->getInstrInfo();
3675f757f3fSDimitry Andric   TRI = Subtarget->getRegisterInfo();
3685f757f3fSDimitry Andric 
3695f757f3fSDimitry Andric   SmallVector<MachineBasicBlock::instr_iterator> PAuthPseudoInstrs;
3705f757f3fSDimitry Andric   SmallVector<MachineBasicBlock::instr_iterator> TailCallInstrs;
3715f757f3fSDimitry Andric 
3725f757f3fSDimitry Andric   bool Modified = false;
3735f757f3fSDimitry Andric   bool HasAuthenticationInstrs = false;
3745f757f3fSDimitry Andric 
3755f757f3fSDimitry Andric   for (auto &MBB : MF) {
3765f757f3fSDimitry Andric     // Using instr_iterator to catch unsupported bundled TCRETURN* instructions
3775f757f3fSDimitry Andric     // instead of just skipping them.
3785f757f3fSDimitry Andric     for (auto &MI : MBB.instrs()) {
3795f757f3fSDimitry Andric       switch (MI.getOpcode()) {
3805f757f3fSDimitry Andric       default:
3815f757f3fSDimitry Andric         // Bundled TCRETURN* instructions (such as created by KCFI)
3825f757f3fSDimitry Andric         // are not supported yet, but no support is required if no
3835f757f3fSDimitry Andric         // PAUTH_EPILOGUE instructions exist in the same function.
3845f757f3fSDimitry Andric         // Skip the BUNDLE instruction itself (actual bundled instructions
3855f757f3fSDimitry Andric         // follow it in the instruction list).
3865f757f3fSDimitry Andric         if (MI.isBundle())
3875f757f3fSDimitry Andric           continue;
3885f757f3fSDimitry Andric         if (AArch64InstrInfo::isTailCallReturnInst(MI))
3895f757f3fSDimitry Andric           TailCallInstrs.push_back(MI.getIterator());
3905f757f3fSDimitry Andric         break;
3915f757f3fSDimitry Andric       case AArch64::PAUTH_PROLOGUE:
3925f757f3fSDimitry Andric       case AArch64::PAUTH_EPILOGUE:
3935f757f3fSDimitry Andric         assert(!MI.isBundled());
3945f757f3fSDimitry Andric         PAuthPseudoInstrs.push_back(MI.getIterator());
3955f757f3fSDimitry Andric         break;
3965f757f3fSDimitry Andric       }
3975f757f3fSDimitry Andric     }
3985f757f3fSDimitry Andric   }
3995f757f3fSDimitry Andric 
4005f757f3fSDimitry Andric   for (auto It : PAuthPseudoInstrs) {
4015f757f3fSDimitry Andric     switch (It->getOpcode()) {
4025f757f3fSDimitry Andric     case AArch64::PAUTH_PROLOGUE:
4035f757f3fSDimitry Andric       signLR(MF, It);
4045f757f3fSDimitry Andric       break;
4055f757f3fSDimitry Andric     case AArch64::PAUTH_EPILOGUE:
4065f757f3fSDimitry Andric       authenticateLR(MF, It);
4075f757f3fSDimitry Andric       HasAuthenticationInstrs = true;
4085f757f3fSDimitry Andric       break;
4095f757f3fSDimitry Andric     default:
4105f757f3fSDimitry Andric       llvm_unreachable("Unhandled opcode");
4115f757f3fSDimitry Andric     }
4125f757f3fSDimitry Andric     It->eraseFromParent();
4135f757f3fSDimitry Andric     Modified = true;
4145f757f3fSDimitry Andric   }
4155f757f3fSDimitry Andric 
4165f757f3fSDimitry Andric   // FIXME Do we need to emit any PAuth-related epilogue code at all
4175f757f3fSDimitry Andric   //       when SCS is enabled?
4185f757f3fSDimitry Andric   if (HasAuthenticationInstrs &&
4195f757f3fSDimitry Andric       !MFnI->needsShadowCallStackPrologueEpilogue(MF)) {
4205f757f3fSDimitry Andric     for (auto TailCall : TailCallInstrs) {
4215f757f3fSDimitry Andric       assert(!TailCall->isBundled() && "Not yet supported");
4225f757f3fSDimitry Andric       Modified |= checkAuthenticatedLR(TailCall);
4235f757f3fSDimitry Andric     }
4245f757f3fSDimitry Andric   }
4255f757f3fSDimitry Andric 
4265f757f3fSDimitry Andric   return Modified;
4275f757f3fSDimitry Andric }
428