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 | |
31 | AArch64PointerAuth() : MachineFunctionPass(ID) {} |
32 | |
33 | bool runOnMachineFunction(MachineFunction &MF) override; |
34 | |
35 | 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 | |
54 | FunctionPass *llvm::createAArch64PointerAuthPass() { |
55 | return new AArch64PointerAuth(); |
56 | } |
57 | |
58 | char AArch64PointerAuth::ID = 0; |
59 | |
60 | static 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 | |
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. |
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: *TII, MBB, I: 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(BB&: MBB, I: MBBI, MIMD: DL, MCID: TII->get(Opcode: AArch64::PACM)).setMIFlag(Flags); |
92 | } |
93 | |
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 | |
107 | 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(BB&: MBB, I: MBBI, MIMD: DL, MCID: TII->get(Opcode: 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, Flags: MachineInstr::FrameSetup, EmitCFI); |
135 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, |
136 | MCID: TII->get(Opcode: MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC |
137 | : AArch64::PACIASPPC)) |
138 | .setMIFlag(MachineInstr::FrameSetup) |
139 | ->setPreInstrSymbol(MF, Symbol: MFnI.getSigningInstrLabel()); |
140 | } else { |
141 | BuildPACM(Subtarget: *Subtarget, MBB, MBBI, DL, Flags: MachineInstr::FrameSetup); |
142 | if (MFnI.branchProtectionPAuthLR()) |
143 | emitPACCFI(MBB, MBBI, Flags: MachineInstr::FrameSetup, EmitCFI); |
144 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, |
145 | MCID: TII->get(Opcode: MFnI.shouldSignWithBKey() ? AArch64::PACIBSP |
146 | : AArch64::PACIASP)) |
147 | .setMIFlag(MachineInstr::FrameSetup) |
148 | ->setPreInstrSymbol(MF, Symbol: MFnI.getSigningInstrLabel()); |
149 | if (!MFnI.branchProtectionPAuthLR()) |
150 | emitPACCFI(MBB, MBBI, Flags: MachineInstr::FrameSetup, EmitCFI); |
151 | } |
152 | |
153 | if (!EmitCFI && NeedsWinCFI) { |
154 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: TII->get(Opcode: AArch64::SEH_PACSignLR)) |
155 | .setMIFlag(MachineInstr::FrameSetup); |
156 | } |
157 | } |
158 | |
159 | 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(Kind: Attribute::ShadowCallStack)) { |
186 | if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) { |
187 | assert(PACSym && "No PAC instruction to refer to" ); |
188 | emitPACSymOffsetIntoX16(TII: *TII, MBB, I: MBBI, DL, PACSym); |
189 | BuildMI(BB&: MBB, I: TI, MIMD: DL, |
190 | MCID: TII->get(Opcode: UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi)) |
191 | .addSym(Sym: PACSym) |
192 | .copyImplicitOps(OtherMI: *MBBI) |
193 | .setMIFlag(MachineInstr::FrameDestroy); |
194 | } else { |
195 | BuildPACM(Subtarget: *Subtarget, MBB, MBBI: TI, DL, Flags: MachineInstr::FrameDestroy, PACSym); |
196 | BuildMI(BB&: MBB, I: TI, MIMD: DL, MCID: TII->get(Opcode: UseBKey ? AArch64::RETAB : AArch64::RETAA)) |
197 | .copyImplicitOps(OtherMI: *MBBI) |
198 | .setMIFlag(MachineInstr::FrameDestroy); |
199 | } |
200 | MBB.erase(I: TI); |
201 | } else { |
202 | if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) { |
203 | assert(PACSym && "No PAC instruction to refer to" ); |
204 | emitPACSymOffsetIntoX16(TII: *TII, MBB, I: MBBI, DL, PACSym); |
205 | emitPACCFI(MBB, MBBI, Flags: MachineInstr::FrameDestroy, EmitCFI: EmitAsyncCFI); |
206 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, |
207 | MCID: TII->get(Opcode: UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi)) |
208 | .addSym(Sym: PACSym) |
209 | .setMIFlag(MachineInstr::FrameDestroy); |
210 | } else { |
211 | BuildPACM(Subtarget: *Subtarget, MBB, MBBI, DL, Flags: MachineInstr::FrameDestroy, PACSym); |
212 | if (MFnI->branchProtectionPAuthLR()) |
213 | emitPACCFI(MBB, MBBI, Flags: MachineInstr::FrameDestroy, EmitCFI: EmitAsyncCFI); |
214 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, |
215 | MCID: TII->get(Opcode: UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP)) |
216 | .setMIFlag(MachineInstr::FrameDestroy); |
217 | if (!MFnI->branchProtectionPAuthLR()) |
218 | emitPACCFI(MBB, MBBI, Flags: MachineInstr::FrameDestroy, EmitCFI: EmitAsyncCFI); |
219 | } |
220 | |
221 | if (NeedsWinCFI) { |
222 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: TII->get(Opcode: AArch64::SEH_PACSignLR)) |
223 | .setMIFlag(MachineInstr::FrameDestroy); |
224 | } |
225 | } |
226 | } |
227 | |
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 | |
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(Elt: 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, MBBI: It); |
268 | break; |
269 | case AArch64::PAUTH_EPILOGUE: |
270 | authenticateLR(MF, MBBI: It); |
271 | break; |
272 | default: |
273 | llvm_unreachable("Unhandled opcode" ); |
274 | } |
275 | It->eraseFromParent(); |
276 | Modified = true; |
277 | } |
278 | |
279 | return Modified; |
280 | } |
281 | |