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(MBB, I, DL, TII.get(AArch64::ADRP), AArch64::X16) 65 .addSym(PACSym, AArch64II::MO_PAGE); 66 BuildMI(MBB, I, DL, TII.get(AArch64::ADDXri), AArch64::X16) 67 .addReg(AArch64::X16) 68 .addSym(PACSym, AArch64II::MO_PAGEOFF | AArch64II::MO_NC) 69 .addImm(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, MBB, 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(MBB, MBBI, DL, TII->get(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(MBB, MBBI, DL, TII->get(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, MachineInstr::FrameSetup, EmitCFI); 135 BuildMI(MBB, MBBI, DL, 136 TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC 137 : AArch64::PACIASPPC)) 138 .setMIFlag(MachineInstr::FrameSetup) 139 ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel()); 140 } else { 141 BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup); 142 if (MFnI.branchProtectionPAuthLR()) 143 emitPACCFI(MBB, MBBI, MachineInstr::FrameSetup, EmitCFI); 144 BuildMI(MBB, MBBI, DL, 145 TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP 146 : AArch64::PACIASP)) 147 .setMIFlag(MachineInstr::FrameSetup) 148 ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel()); 149 if (!MFnI.branchProtectionPAuthLR()) 150 emitPACCFI(MBB, MBBI, MachineInstr::FrameSetup, EmitCFI); 151 } 152 153 if (!EmitCFI && NeedsWinCFI) { 154 BuildMI(MBB, MBBI, DL, TII->get(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(Attribute::ShadowCallStack)) { 186 if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) { 187 assert(PACSym && "No PAC instruction to refer to"); 188 emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym); 189 BuildMI(MBB, TI, DL, 190 TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi)) 191 .addSym(PACSym) 192 .copyImplicitOps(*MBBI) 193 .setMIFlag(MachineInstr::FrameDestroy); 194 } else { 195 BuildPACM(*Subtarget, MBB, TI, DL, MachineInstr::FrameDestroy, PACSym); 196 BuildMI(MBB, TI, DL, TII->get(UseBKey ? AArch64::RETAB : AArch64::RETAA)) 197 .copyImplicitOps(*MBBI) 198 .setMIFlag(MachineInstr::FrameDestroy); 199 } 200 MBB.erase(TI); 201 } else { 202 if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) { 203 assert(PACSym && "No PAC instruction to refer to"); 204 emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym); 205 emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI); 206 BuildMI(MBB, MBBI, DL, 207 TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi)) 208 .addSym(PACSym) 209 .setMIFlag(MachineInstr::FrameDestroy); 210 } else { 211 BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym); 212 if (MFnI->branchProtectionPAuthLR()) 213 emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI); 214 BuildMI(MBB, MBBI, DL, 215 TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP)) 216 .setMIFlag(MachineInstr::FrameDestroy); 217 if (!MFnI->branchProtectionPAuthLR()) 218 emitPACCFI(MBB, MBBI, MachineInstr::FrameDestroy, EmitAsyncCFI); 219 } 220 221 if (NeedsWinCFI) { 222 BuildMI(MBB, MBBI, DL, TII->get(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(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, It); 268 break; 269 case AArch64::PAUTH_EPILOGUE: 270 authenticateLR(MF, It); 271 break; 272 default: 273 llvm_unreachable("Unhandled opcode"); 274 } 275 It->eraseFromParent(); 276 Modified = true; 277 } 278 279 return Modified; 280 } 281