xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- AArch64PointerAuth.cpp -- Harden code using PAuth ------------------==//
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 #include "AArch64PointerAuth.h"
10 
11 #include "AArch64.h"
12 #include "AArch64InstrInfo.h"
13 #include "AArch64MachineFunctionInfo.h"
14 #include "AArch64Subtarget.h"
15 #include "llvm/CodeGen/CFIInstBuilder.h"
16 #include "llvm/CodeGen/MachineBasicBlock.h"
17 #include "llvm/CodeGen/MachineInstrBuilder.h"
18 #include "llvm/CodeGen/MachineModuleInfo.h"
19 
20 using namespace llvm;
21 using namespace llvm::AArch64PAuth;
22 
23 #define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication"
24 
25 namespace {
26 
27 class AArch64PointerAuth : public MachineFunctionPass {
28 public:
29   static char ID;
30 
AArch64PointerAuth()31   AArch64PointerAuth() : MachineFunctionPass(ID) {}
32 
33   bool runOnMachineFunction(MachineFunction &MF) override;
34 
getPassName() const35   StringRef getPassName() const override { return AARCH64_POINTER_AUTH_NAME; }
36 
37 private:
38   const AArch64Subtarget *Subtarget = nullptr;
39   const AArch64InstrInfo *TII = nullptr;
40 
41   void signLR(MachineFunction &MF, MachineBasicBlock::iterator MBBI) const;
42 
43   void authenticateLR(MachineFunction &MF,
44                       MachineBasicBlock::iterator MBBI) const;
45 
46   bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const;
47 };
48 
49 } // end anonymous namespace
50 
51 INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth",
52                 AARCH64_POINTER_AUTH_NAME, false, false)
53 
createAArch64PointerAuthPass()54 FunctionPass *llvm::createAArch64PointerAuthPass() {
55   return new AArch64PointerAuth();
56 }
57 
58 char AArch64PointerAuth::ID = 0;
59 
emitPACSymOffsetIntoX16(const TargetInstrInfo & TII,MachineBasicBlock & MBB,MachineBasicBlock::iterator I,DebugLoc DL,MCSymbol * PACSym)60 static void emitPACSymOffsetIntoX16(const TargetInstrInfo &TII,
61                                     MachineBasicBlock &MBB,
62                                     MachineBasicBlock::iterator I, DebugLoc DL,
63                                     MCSymbol *PACSym) {
64   BuildMI(MBB, I, DL, TII.get(AArch64::ADRP), AArch64::X16)
65       .addSym(PACSym, AArch64II::MO_PAGE);
66   BuildMI(MBB, I, DL, TII.get(AArch64::ADDXri), AArch64::X16)
67       .addReg(AArch64::X16)
68       .addSym(PACSym, AArch64II::MO_PAGEOFF | AArch64II::MO_NC)
69       .addImm(0);
70 }
71 
72 // Where PAuthLR support is not known at compile time, it is supported using
73 // PACM. PACM is in the hint space so has no effect when PAuthLR is not
74 // supported by the hardware, but will alter the behaviour of PACI*SP, AUTI*SP
75 // and RETAA/RETAB if the hardware supports PAuthLR.
BuildPACM(const AArch64Subtarget & Subtarget,MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,DebugLoc DL,MachineInstr::MIFlag Flags,MCSymbol * PACSym=nullptr)76 static void BuildPACM(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB,
77                       MachineBasicBlock::iterator MBBI, DebugLoc DL,
78                       MachineInstr::MIFlag Flags, MCSymbol *PACSym = nullptr) {
79   const TargetInstrInfo *TII = Subtarget.getInstrInfo();
80   auto &MFnI = *MBB.getParent()->getInfo<AArch64FunctionInfo>();
81 
82   // Offset to PAC*SP using ADRP + ADD.
83   if (PACSym) {
84     assert(Flags == MachineInstr::FrameDestroy);
85     emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym);
86   }
87 
88   // Only emit PACM if -mbranch-protection has +pc and the target does not
89   // have feature +pauth-lr.
90   if (MFnI.branchProtectionPAuthLR() && !Subtarget.hasPAuthLR())
91     BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACM)).setMIFlag(Flags);
92 }
93 
emitPACCFI(MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,MachineInstr::MIFlag Flags,bool EmitCFI)94 static void emitPACCFI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
95                        MachineInstr::MIFlag Flags, bool EmitCFI) {
96   if (!EmitCFI)
97     return;
98 
99   auto &MF = *MBB.getParent();
100   auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
101 
102   CFIInstBuilder CFIBuilder(MBB, MBBI, Flags);
103   MFnI.branchProtectionPAuthLR() ? CFIBuilder.buildNegateRAStateWithPC()
104                                  : CFIBuilder.buildNegateRAState();
105 }
106 
signLR(MachineFunction & MF,MachineBasicBlock::iterator MBBI) const107 void AArch64PointerAuth::signLR(MachineFunction &MF,
108                                 MachineBasicBlock::iterator MBBI) const {
109   auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
110   bool UseBKey = MFnI.shouldSignWithBKey();
111   bool EmitCFI = MFnI.needsDwarfUnwindInfo(MF);
112   bool NeedsWinCFI = MF.hasWinCFI();
113 
114   MachineBasicBlock &MBB = *MBBI->getParent();
115 
116   // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
117   DebugLoc DL;
118 
119   if (UseBKey) {
120     BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
121         .setMIFlag(MachineInstr::FrameSetup);
122   }
123 
124   // PAuthLR authentication instructions need to know the value of PC at the
125   // point of signing (PACI*).
126   if (MFnI.branchProtectionPAuthLR()) {
127     MCSymbol *PACSym = MF.getContext().createTempSymbol();
128     MFnI.setSigningInstrLabel(PACSym);
129   }
130 
131   // No SEH opcode for this one; it doesn't materialize into an
132   // instruction on Windows.
133   if (MFnI.branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
134     emitPACCFI(MBB, MBBI, MachineInstr::FrameSetup, EmitCFI);
135     BuildMI(MBB, MBBI, DL,
136             TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC
137                                                : AArch64::PACIASPPC))
138         .setMIFlag(MachineInstr::FrameSetup)
139         ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
140   } else {
141     BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup);
142     if (MFnI.branchProtectionPAuthLR())
143       emitPACCFI(MBB, MBBI, MachineInstr::FrameSetup, EmitCFI);
144     BuildMI(MBB, MBBI, DL,
145             TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP
146                                                : AArch64::PACIASP))
147         .setMIFlag(MachineInstr::FrameSetup)
148         ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
149     if (!MFnI.branchProtectionPAuthLR())
150       emitPACCFI(MBB, MBBI, MachineInstr::FrameSetup, EmitCFI);
151   }
152 
153   if (!EmitCFI && NeedsWinCFI) {
154     BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
155         .setMIFlag(MachineInstr::FrameSetup);
156   }
157 }
158 
authenticateLR(MachineFunction & MF,MachineBasicBlock::iterator MBBI) const159 void AArch64PointerAuth::authenticateLR(
160     MachineFunction &MF, MachineBasicBlock::iterator MBBI) const {
161   const AArch64FunctionInfo *MFnI = MF.getInfo<AArch64FunctionInfo>();
162   bool UseBKey = MFnI->shouldSignWithBKey();
163   bool EmitAsyncCFI = MFnI->needsAsyncDwarfUnwindInfo(MF);
164   bool NeedsWinCFI = MF.hasWinCFI();
165 
166   MachineBasicBlock &MBB = *MBBI->getParent();
167   DebugLoc DL = MBBI->getDebugLoc();
168   // MBBI points to a PAUTH_EPILOGUE instruction to be replaced and
169   // TI points to a terminator instruction that may or may not be combined.
170   // Note that inserting new instructions "before MBBI" and "before TI" is
171   // not the same because if ShadowCallStack is enabled, its instructions
172   // are placed between MBBI and TI.
173   MachineBasicBlock::iterator TI = MBB.getFirstInstrTerminator();
174 
175   // The AUTIASP instruction assembles to a hint instruction before v8.3a so
176   // this instruction can safely used for any v8a architecture.
177   // From v8.3a onwards there are optimised authenticate LR and return
178   // instructions, namely RETA{A,B}, that can be used instead. In this case the
179   // DW_CFA_AARCH64_negate_ra_state can't be emitted.
180   bool TerminatorIsCombinable =
181       TI != MBB.end() && TI->getOpcode() == AArch64::RET;
182   MCSymbol *PACSym = MFnI->getSigningInstrLabel();
183 
184   if (Subtarget->hasPAuth() && TerminatorIsCombinable && !NeedsWinCFI &&
185       !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) {
186     if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
187       assert(PACSym && "No PAC instruction to refer to");
188       emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym);
189       BuildMI(MBB, TI, DL,
190               TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi))
191           .addSym(PACSym)
192           .copyImplicitOps(*MBBI)
193           .setMIFlag(MachineInstr::FrameDestroy);
194     } else {
195       BuildPACM(*Subtarget, MBB, TI, DL, MachineInstr::FrameDestroy, PACSym);
196       BuildMI(MBB, TI, DL, TII->get(UseBKey ? AArch64::RETAB : AArch64::RETAA))
197           .copyImplicitOps(*MBBI)
198           .setMIFlag(MachineInstr::FrameDestroy);
199     }
200     MBB.erase(TI);
201   } else {
202     if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
203       assert(PACSym && "No PAC instruction to refer to");
204       emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym);
205       emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI);
206       BuildMI(MBB, MBBI, DL,
207               TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi))
208           .addSym(PACSym)
209           .setMIFlag(MachineInstr::FrameDestroy);
210     } else {
211       BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym);
212       if (MFnI->branchProtectionPAuthLR())
213         emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI);
214       BuildMI(MBB, MBBI, DL,
215               TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP))
216           .setMIFlag(MachineInstr::FrameDestroy);
217       if (!MFnI->branchProtectionPAuthLR())
218         emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI);
219     }
220 
221     if (NeedsWinCFI) {
222       BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
223           .setMIFlag(MachineInstr::FrameDestroy);
224     }
225   }
226 }
227 
getCheckerSizeInBytes(AuthCheckMethod Method)228 unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) {
229   switch (Method) {
230   case AuthCheckMethod::None:
231     return 0;
232   case AuthCheckMethod::DummyLoad:
233     return 4;
234   case AuthCheckMethod::HighBitsNoTBI:
235     return 12;
236   case AuthCheckMethod::XPACHint:
237   case AuthCheckMethod::XPAC:
238     return 20;
239   }
240   llvm_unreachable("Unknown AuthCheckMethod enum");
241 }
242 
runOnMachineFunction(MachineFunction & MF)243 bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) {
244   Subtarget = &MF.getSubtarget<AArch64Subtarget>();
245   TII = Subtarget->getInstrInfo();
246 
247   SmallVector<MachineBasicBlock::instr_iterator> PAuthPseudoInstrs;
248 
249   bool Modified = false;
250 
251   for (auto &MBB : MF) {
252     for (auto &MI : MBB) {
253       switch (MI.getOpcode()) {
254       default:
255         break;
256       case AArch64::PAUTH_PROLOGUE:
257       case AArch64::PAUTH_EPILOGUE:
258         PAuthPseudoInstrs.push_back(MI.getIterator());
259         break;
260       }
261     }
262   }
263 
264   for (auto It : PAuthPseudoInstrs) {
265     switch (It->getOpcode()) {
266     case AArch64::PAUTH_PROLOGUE:
267       signLR(MF, It);
268       break;
269     case AArch64::PAUTH_EPILOGUE:
270       authenticateLR(MF, It);
271       break;
272     default:
273       llvm_unreachable("Unhandled opcode");
274     }
275     It->eraseFromParent();
276     Modified = true;
277   }
278 
279   return Modified;
280 }
281