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