LLVM 18.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 /// An immediate operand passed to BRK instruction, if it is ever emitted.
38 const unsigned BrkOperand = 0xc471;
39
40 const AArch64Subtarget *Subtarget = nullptr;
41 const AArch64InstrInfo *TII = nullptr;
42 const AArch64RegisterInfo *TRI = nullptr;
43
44 void signLR(MachineFunction &MF, MachineBasicBlock::iterator MBBI) const;
45
46 void authenticateLR(MachineFunction &MF,
48
49 bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const;
50};
51
52} // end anonymous namespace
53
54INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth",
55 AARCH64_POINTER_AUTH_NAME, false, false)
56
58 return new AArch64PointerAuth();
59}
60
61char AArch64PointerAuth::ID = 0;
62
63void AArch64PointerAuth::signLR(MachineFunction &MF,
66 bool UseBKey = MFnI->shouldSignWithBKey();
67 bool EmitCFI = MFnI->needsDwarfUnwindInfo(MF);
68 bool NeedsWinCFI = MF.hasWinCFI();
69
71
72 // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
74
75 if (UseBKey) {
76 BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
78 }
79
80 // No SEH opcode for this one; it doesn't materialize into an
81 // instruction on Windows.
83 TII->get(UseBKey ? AArch64::PACIBSP : AArch64::PACIASP))
85
86 if (EmitCFI) {
87 unsigned CFIIndex =
89 BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
90 .addCFIIndex(CFIIndex)
92 } else if (NeedsWinCFI) {
93 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
95 }
96}
97
98void AArch64PointerAuth::authenticateLR(
101 bool UseBKey = MFnI->shouldSignWithBKey();
102 bool EmitAsyncCFI = MFnI->needsAsyncDwarfUnwindInfo(MF);
103 bool NeedsWinCFI = MF.hasWinCFI();
104
106 DebugLoc DL = MBBI->getDebugLoc();
107 // MBBI points to a PAUTH_EPILOGUE instruction to be replaced and
108 // TI points to a terminator instruction that may or may not be combined.
109 // Note that inserting new instructions "before MBBI" and "before TI" is
110 // not the same because if ShadowCallStack is enabled, its instructions
111 // are placed between MBBI and TI.
113
114 // The AUTIASP instruction assembles to a hint instruction before v8.3a so
115 // this instruction can safely used for any v8a architecture.
116 // From v8.3a onwards there are optimised authenticate LR and return
117 // instructions, namely RETA{A,B}, that can be used instead. In this case the
118 // DW_CFA_AARCH64_negate_ra_state can't be emitted.
119 bool TerminatorIsCombinable =
120 TI != MBB.end() && TI->getOpcode() == AArch64::RET;
121 if (Subtarget->hasPAuth() && TerminatorIsCombinable && !NeedsWinCFI &&
122 !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) {
123 unsigned CombinedRetOpcode = UseBKey ? AArch64::RETAB : AArch64::RETAA;
124 BuildMI(MBB, TI, DL, TII->get(CombinedRetOpcode)).copyImplicitOps(*TI);
125 MBB.erase(TI);
126 } else {
127 unsigned AutOpcode = UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP;
128 BuildMI(MBB, MBBI, DL, TII->get(AutOpcode))
130
131 if (EmitAsyncCFI) {
132 unsigned CFIIndex =
134 BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
135 .addCFIIndex(CFIIndex)
137 }
138 if (NeedsWinCFI) {
139 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
141 }
142 }
143}
144
145namespace {
146
147// Mark dummy LDR instruction as volatile to prevent removing it as dead code.
148MachineMemOperand *createCheckMemOperand(MachineFunction &MF,
149 const AArch64Subtarget &Subtarget) {
150 MachinePointerInfo PointerInfo(Subtarget.getAddressCheckPSV());
151 auto MOVolatileLoad =
153
154 return MF.getMachineMemOperand(PointerInfo, MOVolatileLoad, 4, Align(4));
155}
156
157} // namespace
158
161 Register AuthenticatedReg, Register TmpReg, bool UseIKey, unsigned BrkImm) {
162
165 const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
166 const AArch64InstrInfo *TII = Subtarget.getInstrInfo();
167 DebugLoc DL = MBBI->getDebugLoc();
168
169 // First, handle the methods not requiring creating extra MBBs.
170 switch (Method) {
171 default:
172 break;
173 case AuthCheckMethod::None:
174 return MBB;
175 case AuthCheckMethod::DummyLoad:
176 BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRWui), getWRegFromXReg(TmpReg))
177 .addReg(AArch64::LR)
178 .addImm(0)
179 .addMemOperand(createCheckMemOperand(MF, Subtarget));
180 return MBB;
181 }
182
183 // Control flow has to be changed, so arrange new MBBs.
184
185 // At now, at least an AUT* instruction is expected before MBBI
186 assert(MBBI != MBB.begin() &&
187 "Cannot insert the check at the very beginning of MBB");
188 // The block to insert check into.
189 MachineBasicBlock *CheckBlock = &MBB;
190 // The remaining part of the original MBB that is executed on success.
191 MachineBasicBlock *SuccessBlock = MBB.splitAt(*std::prev(MBBI));
192
193 // The block that explicitly generates a break-point exception on failure.
194 MachineBasicBlock *BreakBlock =
196 MF.push_back(BreakBlock);
197 MBB.splitSuccessor(SuccessBlock, BreakBlock);
198
199 assert(CheckBlock->getFallThrough() == SuccessBlock);
200 BuildMI(BreakBlock, DL, TII->get(AArch64::BRK)).addImm(BrkImm);
201
202 switch (Method) {
203 case AuthCheckMethod::None:
204 case AuthCheckMethod::DummyLoad:
205 llvm_unreachable("Should be handled above");
206 case AuthCheckMethod::HighBitsNoTBI:
207 BuildMI(CheckBlock, DL, TII->get(AArch64::EORXrs), TmpReg)
208 .addReg(AuthenticatedReg)
209 .addReg(AuthenticatedReg)
210 .addImm(1);
211 BuildMI(CheckBlock, DL, TII->get(AArch64::TBNZX))
212 .addReg(TmpReg)
213 .addImm(62)
214 .addMBB(BreakBlock);
215 return *SuccessBlock;
216 case AuthCheckMethod::XPACHint:
217 assert(AuthenticatedReg == AArch64::LR &&
218 "XPACHint mode is only compatible with checking the LR register");
219 assert(UseIKey && "XPACHint mode is only compatible with I-keys");
220 BuildMI(CheckBlock, DL, TII->get(AArch64::ORRXrs), TmpReg)
221 .addReg(AArch64::XZR)
222 .addReg(AArch64::LR)
223 .addImm(0);
224 BuildMI(CheckBlock, DL, TII->get(AArch64::XPACLRI));
225 BuildMI(CheckBlock, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR)
226 .addReg(TmpReg)
227 .addReg(AArch64::LR)
228 .addImm(0);
229 BuildMI(CheckBlock, DL, TII->get(AArch64::Bcc))
231 .addMBB(BreakBlock);
232 return *SuccessBlock;
233 }
234 llvm_unreachable("Unknown AuthCheckMethod enum");
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 return 20;
247 }
248 llvm_unreachable("Unknown AuthCheckMethod enum");
249}
250
251bool AArch64PointerAuth::checkAuthenticatedLR(
254
255 if (Method == AuthCheckMethod::None)
256 return false;
257
258 // FIXME If FEAT_FPAC is implemented by the CPU, this check can be skipped.
259
260 assert(!TI->getMF()->hasWinCFI() && "WinCFI is not yet supported");
261
262 // The following code may create a signing oracle:
263 //
264 // <authenticate LR>
265 // TCRETURN ; the callee may sign and spill the LR in its prologue
266 //
267 // To avoid generating a signing oracle, check the authenticated value
268 // before possibly re-signing it in the callee, as follows:
269 //
270 // <authenticate LR>
271 // <check if LR contains a valid address>
272 // b.<cond> break_block
273 // ret_block:
274 // TCRETURN
275 // break_block:
276 // brk <BrkOperand>
277 //
278 // or just
279 //
280 // <authenticate LR>
281 // ldr tmp, [lr]
282 // TCRETURN
283
284 // TmpReg is chosen assuming X16 and X17 are dead after TI.
286 "Tail call is expected");
287 Register TmpReg =
288 TI->readsRegister(AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16;
289 assert(!TI->readsRegister(TmpReg, TRI) &&
290 "More than a single register is used by TCRETURN");
291
292 checkAuthenticatedRegister(TI, Method, AArch64::LR, TmpReg, /*UseIKey=*/true,
293 BrkOperand);
294
295 return true;
296}
297
298bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) {
299 const auto *MFnI = MF.getInfo<AArch64FunctionInfo>();
300
301 Subtarget = &MF.getSubtarget<AArch64Subtarget>();
302 TII = Subtarget->getInstrInfo();
303 TRI = Subtarget->getRegisterInfo();
304
307
308 bool Modified = false;
309 bool HasAuthenticationInstrs = false;
310
311 for (auto &MBB : MF) {
312 // Using instr_iterator to catch unsupported bundled TCRETURN* instructions
313 // instead of just skipping them.
314 for (auto &MI : MBB.instrs()) {
315 switch (MI.getOpcode()) {
316 default:
317 // Bundled TCRETURN* instructions (such as created by KCFI)
318 // are not supported yet, but no support is required if no
319 // PAUTH_EPILOGUE instructions exist in the same function.
320 // Skip the BUNDLE instruction itself (actual bundled instructions
321 // follow it in the instruction list).
322 if (MI.isBundle())
323 continue;
325 TailCallInstrs.push_back(MI.getIterator());
326 break;
327 case AArch64::PAUTH_PROLOGUE:
328 case AArch64::PAUTH_EPILOGUE:
329 assert(!MI.isBundled());
330 PAuthPseudoInstrs.push_back(MI.getIterator());
331 break;
332 }
333 }
334 }
335
336 for (auto It : PAuthPseudoInstrs) {
337 switch (It->getOpcode()) {
338 case AArch64::PAUTH_PROLOGUE:
339 signLR(MF, It);
340 break;
341 case AArch64::PAUTH_EPILOGUE:
342 authenticateLR(MF, It);
343 HasAuthenticationInstrs = true;
344 break;
345 default:
346 llvm_unreachable("Unhandled opcode");
347 }
348 It->eraseFromParent();
349 Modified = true;
350 }
351
352 // FIXME Do we need to emit any PAuth-related epilogue code at all
353 // when SCS is enabled?
354 if (HasAuthenticationInstrs &&
356 for (auto TailCall : TailCallInstrs) {
357 assert(!TailCall->isBundled() && "Not yet supported");
358 Modified |= checkAuthenticatedLR(TailCall);
359 }
360 }
361
362 return Modified;
363}
#define AARCH64_POINTER_AUTH_NAME
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
MachineBasicBlock MachineBasicBlock::iterator MBBI
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
unsigned const TargetRegisterInfo * TRI
#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 needsShadowCallStackPrologueEpilogue(MachineFunction &MF) const
bool needsDwarfUnwindInfo(const MachineFunction &MF) const
bool needsAsyncDwarfUnwindInfo(const MachineFunction &MF) const
static bool isTailCallReturnInst(const MachineInstr &MI)
Returns true if MI is one of the TCRETURN* instructions.
const AArch64InstrInfo * getInstrInfo() const override
const PseudoSourceValue * getAddressCheckPSV() const
AArch64PAuth::AuthCheckMethod getAuthenticatedLRCheckMethod() const
Choose a method of checking LR before performing a tail call.
A debug info location.
Definition: DebugLoc.h:33
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:311
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition: Function.cpp:666
static MCCFIInstruction createNegateRAState(MCSymbol *L, SMLoc Loc={})
.cfi_negate_ra_state AArch64 negate RA state.
Definition: MCDwarf.h:609
MachineBasicBlock * getFallThrough(bool JumpToFallThrough=true)
Return the fallthrough block if the block can implicitly transfer control to the block after it by fa...
const BasicBlock * getBasicBlock() const
Return the LLVM basic block that this instance corresponded to originally.
void splitSuccessor(MachineBasicBlock *Old, MachineBasicBlock *New, bool NormalizeSuccProbs=false)
Split the old successor into old plus new and updates the probability info.
MachineBasicBlock * splitAt(MachineInstr &SplitInst, bool UpdateLiveIns=true, LiveIntervals *LIS=nullptr)
Split a basic block into 2 pieces at SplitPoint.
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...
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags f, uint64_t s, Align base_alignment, const AAMDNodes &AAInfo=AAMDNodes(), const MDNode *Ranges=nullptr, SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
unsigned addFrameInst(const MCCFIInstruction &Inst)
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
void push_back(MachineBasicBlock *MBB)
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...
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
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 & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
const MachineInstrBuilder & setMIFlags(unsigned Flags) const
const MachineInstrBuilder & copyImplicitOps(const MachineInstr &OtherMI) const
Copy all the implicit operands from OtherMI onto this one.
const MachineInstrBuilder & addMemOperand(MachineMemOperand *MMO) const
A description of a memory reference used in the backend.
@ MOVolatile
The memory access is volatile.
@ MOLoad
The memory access reads data.
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:416
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned getCheckerSizeInBytes(AuthCheckMethod Method)
Returns the number of bytes added by checkAuthenticatedRegister.
AuthCheckMethod
Variants of check performed on an authenticated pointer.
MachineBasicBlock & checkAuthenticatedRegister(MachineBasicBlock::iterator MBBI, AuthCheckMethod Method, Register AuthenticatedReg, Register TmpReg, bool UseIKey, unsigned BrkImm)
Explicitly checks that pointer authentication succeeded.
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()
static unsigned getWRegFromXReg(unsigned Reg)
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
This class contains a discriminated union of information about pointers in memory operands,...