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/MachineBasicBlock.h" 16 #include "llvm/CodeGen/MachineInstrBuilder.h" 17 #include "llvm/CodeGen/MachineModuleInfo.h" 18 19 using namespace llvm; 20 using namespace llvm::AArch64PAuth; 21 22 #define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication" 23 24 namespace { 25 26 class AArch64PointerAuth : public MachineFunctionPass { 27 public: 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 36 private: 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, 43 MachineBasicBlock::iterator MBBI) const; 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 57 INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth", 58 AARCH64_POINTER_AUTH_NAME, false, false) 59 60 FunctionPass *llvm::createAArch64PointerAuthPass() { 61 return new AArch64PointerAuth(); 62 } 63 64 char AArch64PointerAuth::ID = 0; 65 66 static void emitPACSymOffsetIntoX16(const TargetInstrInfo &TII, 67 MachineBasicBlock &MBB, 68 MachineBasicBlock::iterator I, DebugLoc DL, 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) 74 .addSym(PACSym, AArch64II::MO_PAGEOFF | AArch64II::MO_NC) 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. 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>(); 87 88 // Offset to PAC*SP using ADRP + ADD. 89 if (PACSym) { 90 assert(Flags == MachineInstr::FrameDestroy); 91 emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, 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 100 static void emitPACCFI(const AArch64Subtarget &Subtarget, 101 MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, 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() 111 ? MCCFIInstruction::createNegateRAStateWithPC(nullptr) 112 : MCCFIInstruction::createNegateRAState(nullptr); 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 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(); 126 127 MachineBasicBlock &MBB = *MBBI->getParent(); 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)) 134 .setMIFlag(MachineInstr::FrameSetup); 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 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()); 153 } else { 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 158 : AArch64::PACIASP)) 159 .setMIFlag(MachineInstr::FrameSetup) 160 ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel()); 161 } 162 163 if (!EmitCFI && NeedsWinCFI) { 164 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR)) 165 .setMIFlag(MachineInstr::FrameSetup); 166 } 167 } 168 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(); 175 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(); 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"); 198 emitPACSymOffsetIntoX16(*TII, MBB, MBBI, DL, PACSym); 199 BuildMI(MBB, TI, DL, 200 TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi)) 201 .addSym(PACSym) 202 .copyImplicitOps(*MBBI) 203 .setMIFlag(MachineInstr::FrameDestroy); 204 } else { 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); 209 } 210 MBB.erase(TI); 211 } else { 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, 216 EmitAsyncCFI); 217 BuildMI(MBB, MBBI, DL, 218 TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi)) 219 .addSym(PACSym) 220 .setMIFlag(MachineInstr::FrameDestroy); 221 } else { 222 BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym); 223 emitPACCFI(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, 224 EmitAsyncCFI); 225 BuildMI(MBB, MBBI, DL, 226 TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP)) 227 .setMIFlag(MachineInstr::FrameDestroy); 228 } 229 230 if (NeedsWinCFI) { 231 BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR)) 232 .setMIFlag(MachineInstr::FrameDestroy); 233 } 234 } 235 } 236 237 unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) { 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 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(); 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 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); 276 } 277 278 bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) { 279 Subtarget = &MF.getSubtarget<AArch64Subtarget>(); 280 TII = Subtarget->getInstrInfo(); 281 282 SmallVector<MachineBasicBlock::instr_iterator> PAuthPseudoInstrs; 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 } 320