1 //===-- AArch64PointerAuth.cpp -- Harden code using PAuth ------------------==//
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
7 //===----------------------------------------------------------------------===//
9 #include "AArch64PointerAuth.h"
12 #include "AArch64InstrInfo.h"
13 #include "AArch64MachineFunctionInfo.h"
14 #include "AArch64Subtarget.h"
15 #include "llvm/CodeGen/MachineBasicBlock.h"
16 #include "llvm/CodeGen/MachineInstrBuilder.h"
17 #include "llvm/CodeGen/MachineModuleInfo.h"
20 using namespace llvm::AArch64PAuth
;
22 #define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication"
26 class AArch64PointerAuth
: public MachineFunctionPass
{
30 AArch64PointerAuth() : MachineFunctionPass(ID
) {}
32 bool runOnMachineFunction(MachineFunction
&MF
) override
;
34 StringRef
getPassName() const override
{ return AARCH64_POINTER_AUTH_NAME
; }
37 const AArch64Subtarget
*Subtarget
= nullptr;
38 const AArch64InstrInfo
*TII
= nullptr;
40 void signLR(MachineFunction
&MF
, MachineBasicBlock::iterator MBBI
) const;
42 void authenticateLR(MachineFunction
&MF
,
43 MachineBasicBlock::iterator MBBI
) const;
45 /// Stores blend(AddrDisc, IntDisc) to the Result register.
46 void emitBlend(MachineBasicBlock::iterator MBBI
, Register Result
,
47 Register AddrDisc
, unsigned IntDisc
) const;
49 /// Expands PAUTH_BLEND pseudo instruction.
50 void expandPAuthBlend(MachineBasicBlock::iterator MBBI
) const;
52 bool checkAuthenticatedLR(MachineBasicBlock::iterator TI
) const;
55 } // end anonymous namespace
57 INITIALIZE_PASS(AArch64PointerAuth
, "aarch64-ptrauth",
58 AARCH64_POINTER_AUTH_NAME
, false, false)
60 FunctionPass
*llvm::createAArch64PointerAuthPass() {
61 return new AArch64PointerAuth();
64 char AArch64PointerAuth::ID
= 0;
66 static void emitPACSymOffsetIntoX16(const TargetInstrInfo
&TII
,
67 MachineBasicBlock
&MBB
,
68 MachineBasicBlock::iterator I
, DebugLoc DL
,
70 BuildMI(MBB
, I
, DL
, TII
.get(AArch64::ADRP
), AArch64::X16
)
71 .addSym(PACSym
, AArch64II::MO_PAGE
);
72 BuildMI(MBB
, I
, DL
, TII
.get(AArch64::ADDXri
), AArch64::X16
)
74 .addSym(PACSym
, AArch64II::MO_PAGEOFF
| AArch64II::MO_NC
)
78 // Where PAuthLR support is not known at compile time, it is supported using
79 // PACM. PACM is in the hint space so has no effect when PAuthLR is not
80 // supported by the hardware, but will alter the behaviour of PACI*SP, AUTI*SP
81 // and RETAA/RETAB if the hardware supports PAuthLR.
82 static void BuildPACM(const AArch64Subtarget
&Subtarget
, MachineBasicBlock
&MBB
,
83 MachineBasicBlock::iterator MBBI
, DebugLoc DL
,
84 MachineInstr::MIFlag Flags
, MCSymbol
*PACSym
= nullptr) {
85 const TargetInstrInfo
*TII
= Subtarget
.getInstrInfo();
86 auto &MFnI
= *MBB
.getParent()->getInfo
<AArch64FunctionInfo
>();
88 // Offset to PAC*SP using ADRP + ADD.
90 assert(Flags
== MachineInstr::FrameDestroy
);
91 emitPACSymOffsetIntoX16(*TII
, MBB
, MBBI
, DL
, PACSym
);
94 // Only emit PACM if -mbranch-protection has +pc and the target does not
95 // have feature +pauth-lr.
96 if (MFnI
.branchProtectionPAuthLR() && !Subtarget
.hasPAuthLR())
97 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::PACM
)).setMIFlag(Flags
);
100 static void emitPACCFI(const AArch64Subtarget
&Subtarget
,
101 MachineBasicBlock
&MBB
, MachineBasicBlock::iterator MBBI
,
102 DebugLoc DL
, MachineInstr::MIFlag Flags
, bool EmitCFI
) {
106 const TargetInstrInfo
*TII
= Subtarget
.getInstrInfo();
107 auto &MF
= *MBB
.getParent();
108 auto &MFnI
= *MF
.getInfo
<AArch64FunctionInfo
>();
110 auto CFIInst
= MFnI
.branchProtectionPAuthLR()
111 ? MCCFIInstruction::createNegateRAStateWithPC(nullptr)
112 : MCCFIInstruction::createNegateRAState(nullptr);
114 unsigned CFIIndex
= MF
.addFrameInst(CFIInst
);
115 BuildMI(MBB
, MBBI
, DL
, TII
->get(TargetOpcode::CFI_INSTRUCTION
))
116 .addCFIIndex(CFIIndex
)
120 void AArch64PointerAuth::signLR(MachineFunction
&MF
,
121 MachineBasicBlock::iterator MBBI
) const {
122 auto &MFnI
= *MF
.getInfo
<AArch64FunctionInfo
>();
123 bool UseBKey
= MFnI
.shouldSignWithBKey();
124 bool EmitCFI
= MFnI
.needsDwarfUnwindInfo(MF
);
125 bool NeedsWinCFI
= MF
.hasWinCFI();
127 MachineBasicBlock
&MBB
= *MBBI
->getParent();
129 // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
133 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::EMITBKEY
))
134 .setMIFlag(MachineInstr::FrameSetup
);
137 // PAuthLR authentication instructions need to know the value of PC at the
138 // point of signing (PACI*).
139 if (MFnI
.branchProtectionPAuthLR()) {
140 MCSymbol
*PACSym
= MF
.getContext().createTempSymbol();
141 MFnI
.setSigningInstrLabel(PACSym
);
144 // No SEH opcode for this one; it doesn't materialize into an
145 // instruction on Windows.
146 if (MFnI
.branchProtectionPAuthLR() && Subtarget
->hasPAuthLR()) {
147 emitPACCFI(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameSetup
, EmitCFI
);
148 BuildMI(MBB
, MBBI
, DL
,
149 TII
->get(MFnI
.shouldSignWithBKey() ? AArch64::PACIBSPPC
150 : AArch64::PACIASPPC
))
151 .setMIFlag(MachineInstr::FrameSetup
)
152 ->setPreInstrSymbol(MF
, MFnI
.getSigningInstrLabel());
154 BuildPACM(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameSetup
);
155 emitPACCFI(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameSetup
, EmitCFI
);
156 BuildMI(MBB
, MBBI
, DL
,
157 TII
->get(MFnI
.shouldSignWithBKey() ? AArch64::PACIBSP
159 .setMIFlag(MachineInstr::FrameSetup
)
160 ->setPreInstrSymbol(MF
, MFnI
.getSigningInstrLabel());
163 if (!EmitCFI
&& NeedsWinCFI
) {
164 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::SEH_PACSignLR
))
165 .setMIFlag(MachineInstr::FrameSetup
);
169 void AArch64PointerAuth::authenticateLR(
170 MachineFunction
&MF
, MachineBasicBlock::iterator MBBI
) const {
171 const AArch64FunctionInfo
*MFnI
= MF
.getInfo
<AArch64FunctionInfo
>();
172 bool UseBKey
= MFnI
->shouldSignWithBKey();
173 bool EmitAsyncCFI
= MFnI
->needsAsyncDwarfUnwindInfo(MF
);
174 bool NeedsWinCFI
= MF
.hasWinCFI();
176 MachineBasicBlock
&MBB
= *MBBI
->getParent();
177 DebugLoc DL
= MBBI
->getDebugLoc();
178 // MBBI points to a PAUTH_EPILOGUE instruction to be replaced and
179 // TI points to a terminator instruction that may or may not be combined.
180 // Note that inserting new instructions "before MBBI" and "before TI" is
181 // not the same because if ShadowCallStack is enabled, its instructions
182 // are placed between MBBI and TI.
183 MachineBasicBlock::iterator TI
= MBB
.getFirstInstrTerminator();
185 // The AUTIASP instruction assembles to a hint instruction before v8.3a so
186 // this instruction can safely used for any v8a architecture.
187 // From v8.3a onwards there are optimised authenticate LR and return
188 // instructions, namely RETA{A,B}, that can be used instead. In this case the
189 // DW_CFA_AARCH64_negate_ra_state can't be emitted.
190 bool TerminatorIsCombinable
=
191 TI
!= MBB
.end() && TI
->getOpcode() == AArch64::RET
;
192 MCSymbol
*PACSym
= MFnI
->getSigningInstrLabel();
194 if (Subtarget
->hasPAuth() && TerminatorIsCombinable
&& !NeedsWinCFI
&&
195 !MF
.getFunction().hasFnAttribute(Attribute::ShadowCallStack
)) {
196 if (MFnI
->branchProtectionPAuthLR() && Subtarget
->hasPAuthLR()) {
197 assert(PACSym
&& "No PAC instruction to refer to");
198 emitPACSymOffsetIntoX16(*TII
, MBB
, MBBI
, DL
, PACSym
);
200 TII
->get(UseBKey
? AArch64::RETABSPPCi
: AArch64::RETAASPPCi
))
202 .copyImplicitOps(*MBBI
)
203 .setMIFlag(MachineInstr::FrameDestroy
);
205 BuildPACM(*Subtarget
, MBB
, TI
, DL
, MachineInstr::FrameDestroy
, PACSym
);
206 BuildMI(MBB
, TI
, DL
, TII
->get(UseBKey
? AArch64::RETAB
: AArch64::RETAA
))
207 .copyImplicitOps(*MBBI
)
208 .setMIFlag(MachineInstr::FrameDestroy
);
212 if (MFnI
->branchProtectionPAuthLR() && Subtarget
->hasPAuthLR()) {
213 assert(PACSym
&& "No PAC instruction to refer to");
214 emitPACSymOffsetIntoX16(*TII
, MBB
, MBBI
, DL
, PACSym
);
215 emitPACCFI(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameDestroy
,
217 BuildMI(MBB
, MBBI
, DL
,
218 TII
->get(UseBKey
? AArch64::AUTIBSPPCi
: AArch64::AUTIASPPCi
))
220 .setMIFlag(MachineInstr::FrameDestroy
);
222 BuildPACM(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameDestroy
, PACSym
);
223 emitPACCFI(*Subtarget
, MBB
, MBBI
, DL
, MachineInstr::FrameDestroy
,
225 BuildMI(MBB
, MBBI
, DL
,
226 TII
->get(UseBKey
? AArch64::AUTIBSP
: AArch64::AUTIASP
))
227 .setMIFlag(MachineInstr::FrameDestroy
);
231 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::SEH_PACSignLR
))
232 .setMIFlag(MachineInstr::FrameDestroy
);
237 unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method
) {
239 case AuthCheckMethod::None
:
241 case AuthCheckMethod::DummyLoad
:
243 case AuthCheckMethod::HighBitsNoTBI
:
245 case AuthCheckMethod::XPACHint
:
246 case AuthCheckMethod::XPAC
:
249 llvm_unreachable("Unknown AuthCheckMethod enum");
252 void AArch64PointerAuth::emitBlend(MachineBasicBlock::iterator MBBI
,
253 Register Result
, Register AddrDisc
,
254 unsigned IntDisc
) const {
255 MachineBasicBlock
&MBB
= *MBBI
->getParent();
256 DebugLoc DL
= MBBI
->getDebugLoc();
258 if (Result
!= AddrDisc
)
259 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::ORRXrs
), Result
)
260 .addReg(AArch64::XZR
)
264 BuildMI(MBB
, MBBI
, DL
, TII
->get(AArch64::MOVKXi
), Result
)
270 void AArch64PointerAuth::expandPAuthBlend(
271 MachineBasicBlock::iterator MBBI
) const {
272 Register ResultReg
= MBBI
->getOperand(0).getReg();
273 Register AddrDisc
= MBBI
->getOperand(1).getReg();
274 unsigned IntDisc
= MBBI
->getOperand(2).getImm();
275 emitBlend(MBBI
, ResultReg
, AddrDisc
, IntDisc
);
278 bool AArch64PointerAuth::runOnMachineFunction(MachineFunction
&MF
) {
279 Subtarget
= &MF
.getSubtarget
<AArch64Subtarget
>();
280 TII
= Subtarget
->getInstrInfo();
282 SmallVector
<MachineBasicBlock::instr_iterator
> PAuthPseudoInstrs
;
284 bool Modified
= false;
286 for (auto &MBB
: MF
) {
287 for (auto &MI
: MBB
) {
288 switch (MI
.getOpcode()) {
291 case AArch64::PAUTH_PROLOGUE
:
292 case AArch64::PAUTH_EPILOGUE
:
293 case AArch64::PAUTH_BLEND
:
294 PAuthPseudoInstrs
.push_back(MI
.getIterator());
300 for (auto It
: PAuthPseudoInstrs
) {
301 switch (It
->getOpcode()) {
302 case AArch64::PAUTH_PROLOGUE
:
305 case AArch64::PAUTH_EPILOGUE
:
306 authenticateLR(MF
, It
);
308 case AArch64::PAUTH_BLEND
:
309 expandPAuthBlend(It
);
312 llvm_unreachable("Unhandled opcode");
314 It
->eraseFromParent();