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