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