LLVM 20.0.0git
AArch64PointerAuth.cpp
Go to the documentation of this file.
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
10
11#include "AArch64.h"
12#include "AArch64InstrInfo.h"
14#include "AArch64Subtarget.h"
18
19using namespace llvm;
20using namespace llvm::AArch64PAuth;
21
22#define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication"
23
24namespace {
25
26class AArch64PointerAuth : public MachineFunctionPass {
27public:
28 static char ID;
29
30 AArch64PointerAuth() : MachineFunctionPass(ID) {}
31
32 bool runOnMachineFunction(MachineFunction &MF) override;
33
34 StringRef getPassName() const override { return AARCH64_POINTER_AUTH_NAME; }
35
36private:
37 const AArch64Subtarget *Subtarget = nullptr;
38 const AArch64InstrInfo *TII = nullptr;
39
40 void signLR(MachineFunction &MF, MachineBasicBlock::iterator MBBI) const;
41
42 void authenticateLR(MachineFunction &MF,
44
45 /// Stores blend(AddrDisc, IntDisc) to the Result register.
46 void emitBlend(MachineBasicBlock::iterator MBBI, Register Result,
47 Register AddrDisc, unsigned IntDisc) const;
48
49 /// Expands PAUTH_BLEND pseudo instruction.
50 void expandPAuthBlend(MachineBasicBlock::iterator MBBI) const;
51
52 bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const;
53};
54
55} // end anonymous namespace
56
57INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth",
58 AARCH64_POINTER_AUTH_NAME, false, false)
59
61 return new AArch64PointerAuth();
62}
63
64char AArch64PointerAuth::ID = 0;
65
69 MCSymbol *PACSym) {
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)
73 .addReg(AArch64::X16)
75 .addImm(0);
76}
77
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.
82static void BuildPACM(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB,
84 MachineInstr::MIFlag Flags, MCSymbol *PACSym = nullptr) {
85 const TargetInstrInfo *TII = Subtarget.getInstrInfo();
86 auto &MFnI = *MBB.getParent()->getInfo<AArch64FunctionInfo>();
87
88 // Offset to PAC*SP using ADRP + ADD.
89 if (PACSym) {
92 }
93
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);
98}
99
100static void emitPACCFI(const AArch64Subtarget &Subtarget,
102 DebugLoc DL, MachineInstr::MIFlag Flags, bool EmitCFI) {
103 if (!EmitCFI)
104 return;
105
106 const TargetInstrInfo *TII = Subtarget.getInstrInfo();
107 auto &MF = *MBB.getParent();
108 auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
109
110 auto CFIInst = MFnI.branchProtectionPAuthLR()
113
114 unsigned CFIIndex = MF.addFrameInst(CFIInst);
115 BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
116 .addCFIIndex(CFIIndex)
117 .setMIFlags(Flags);
118}
119
120void AArch64PointerAuth::signLR(MachineFunction &MF,
122 auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
123 bool UseBKey = MFnI.shouldSignWithBKey();
124 bool EmitCFI = MFnI.needsDwarfUnwindInfo(MF);
125 bool NeedsWinCFI = MF.hasWinCFI();
126
128
129 // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
130 DebugLoc DL;
131
132 if (UseBKey) {
133 BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
135 }
136
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);
142 }
143
144 // No SEH opcode for this one; it doesn't materialize into an
145 // instruction on Windows.
146 if (MFnI.branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
147 BuildMI(MBB, MBBI, DL,
148 TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC
149 : AArch64::PACIASPPC))
151 ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
152 emitPACCFI(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup, EmitCFI);
153 } else {
155 BuildMI(MBB, MBBI, DL,
156 TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP
157 : AArch64::PACIASP))
159 ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
160 emitPACCFI(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup, EmitCFI);
161 }
162
163 if (!EmitCFI && NeedsWinCFI) {
164 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
166 }
167}
168
169void AArch64PointerAuth::authenticateLR(
172 bool UseBKey = MFnI->shouldSignWithBKey();
173 bool EmitAsyncCFI = MFnI->needsAsyncDwarfUnwindInfo(MF);
174 bool NeedsWinCFI = MF.hasWinCFI();
175
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.
184
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();
193
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");
199 BuildMI(MBB, TI, DL,
200 TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi))
201 .addSym(PACSym)
204 } else {
205 BuildPACM(*Subtarget, MBB, TI, DL, MachineInstr::FrameDestroy, PACSym);
206 BuildMI(MBB, TI, DL, TII->get(UseBKey ? AArch64::RETAB : AArch64::RETAA))
209 }
210 MBB.erase(TI);
211 } else {
212 if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
213 assert(PACSym && "No PAC instruction to refer to");
215 BuildMI(MBB, MBBI, DL,
216 TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi))
217 .addSym(PACSym)
220 EmitAsyncCFI);
221 } else {
222 BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym);
223 BuildMI(MBB, MBBI, DL,
224 TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP))
227 EmitAsyncCFI);
228 }
229
230 if (NeedsWinCFI) {
231 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
233 }
234 }
235}
236
238 switch (Method) {
239 case AuthCheckMethod::None:
240 return 0;
241 case AuthCheckMethod::DummyLoad:
242 return 4;
243 case AuthCheckMethod::HighBitsNoTBI:
244 return 12;
245 case AuthCheckMethod::XPACHint:
246 case AuthCheckMethod::XPAC:
247 return 20;
248 }
249 llvm_unreachable("Unknown AuthCheckMethod enum");
250}
251
252void AArch64PointerAuth::emitBlend(MachineBasicBlock::iterator MBBI,
253 Register Result, Register AddrDisc,
254 unsigned IntDisc) const {
256 DebugLoc DL = MBBI->getDebugLoc();
257
258 if (Result != AddrDisc)
259 BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), Result)
260 .addReg(AArch64::XZR)
261 .addReg(AddrDisc)
262 .addImm(0);
263
264 BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), Result)
265 .addReg(Result)
266 .addImm(IntDisc)
267 .addImm(48);
268}
269
270void AArch64PointerAuth::expandPAuthBlend(
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);
276}
277
278bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) {
279 Subtarget = &MF.getSubtarget<AArch64Subtarget>();
280 TII = Subtarget->getInstrInfo();
281
283
284 bool Modified = false;
285
286 for (auto &MBB : MF) {
287 for (auto &MI : MBB) {
288 switch (MI.getOpcode()) {
289 default:
290 break;
291 case AArch64::PAUTH_PROLOGUE:
292 case AArch64::PAUTH_EPILOGUE:
293 case AArch64::PAUTH_BLEND:
294 PAuthPseudoInstrs.push_back(MI.getIterator());
295 break;
296 }
297 }
298 }
299
300 for (auto It : PAuthPseudoInstrs) {
301 switch (It->getOpcode()) {
302 case AArch64::PAUTH_PROLOGUE:
303 signLR(MF, It);
304 break;
305 case AArch64::PAUTH_EPILOGUE:
306 authenticateLR(MF, It);
307 break;
308 case AArch64::PAUTH_BLEND:
309 expandPAuthBlend(It);
310 break;
311 default:
312 llvm_unreachable("Unhandled opcode");
313 }
314 It->eraseFromParent();
315 Modified = true;
316 }
317
318 return Modified;
319}
#define AARCH64_POINTER_AUTH_NAME
static void emitPACCFI(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL, MachineInstr::MIFlag Flags, bool EmitCFI)
static void BuildPACM(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL, MachineInstr::MIFlag Flags, MCSymbol *PACSym=nullptr)
static void emitPACSymOffsetIntoX16(const TargetInstrInfo &TII, MachineBasicBlock &MBB, MachineBasicBlock::iterator I, DebugLoc DL, MCSymbol *PACSym)
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
MachineBasicBlock MachineBasicBlock::iterator MBBI
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition: MD5.cpp:58
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:38
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
AArch64FunctionInfo - This class is derived from MachineFunctionInfo and contains private AArch64-spe...
bool needsAsyncDwarfUnwindInfo(const MachineFunction &MF) const
const AArch64InstrInfo * getInstrInfo() const override
A debug info location.
Definition: DebugLoc.h:33
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:310
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition: Function.cpp:731
static MCCFIInstruction createNegateRAStateWithPC(MCSymbol *L, SMLoc Loc={})
.cfi_negate_ra_state_with_pc AArch64 negate RA state with PC.
Definition: MCDwarf.h:648
static MCCFIInstruction createNegateRAState(MCSymbol *L, SMLoc Loc={})
.cfi_negate_ra_state AArch64 negate RA state.
Definition: MCDwarf.h:643
MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Definition: MCContext.cpp:345
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:41
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
instr_iterator erase(instr_iterator I)
Remove an instruction from the instruction list and delete it.
instr_iterator getFirstInstrTerminator()
Same getFirstTerminator but it ignores bundles and return an instr_iterator instead.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
virtual bool runOnMachineFunction(MachineFunction &MF)=0
runOnMachineFunction - This method must be overloaded to perform the desired machine code transformat...
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MCContext & getContext() const
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineInstrBuilder & addCFIIndex(unsigned CFIIndex) const
const MachineInstrBuilder & setMIFlag(MachineInstr::MIFlag Flag) const
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & setMIFlags(unsigned Flags) const
const MachineInstrBuilder & copyImplicitOps(const MachineInstr &OtherMI) const
Copy all the implicit operands from OtherMI onto this one.
void setPreInstrSymbol(MachineFunction &MF, MCSymbol *Symbol)
Set a symbol that will be emitted just prior to the instruction itself.
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:81
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
TargetInstrInfo - Interface to description of machine instruction set.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ MO_NC
MO_NC - Indicates whether the linker is expected to check the symbol reference for overflow.
@ MO_PAGEOFF
MO_PAGEOFF - A symbol operand with this flag represents the offset of that symbol within a 4K page.
@ MO_PAGE
MO_PAGE - A symbol operand with this flag represents the pc-relative offset of the 4K page containing...
unsigned getCheckerSizeInBytes(AuthCheckMethod Method)
Returns the number of bytes added by checkAuthenticatedRegister.
AuthCheckMethod
Variants of check performed on an authenticated pointer.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
FunctionPass * createAArch64PointerAuthPass()