diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index cf37a984da93f..132d58f3f9f79 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -49,6 +49,7 @@ class MCSymbol; class raw_ostream; namespace bolt { +class BinaryBasicBlock; class BinaryFunction; /// Different types of indirect branches encountered during disassembly. @@ -572,6 +573,11 @@ class MCPlusBuilder { return false; } + virtual MCPhysReg getSignedReg(const MCInst &Inst) const { + llvm_unreachable("not implemented"); + return getNoRegister(); + } + virtual ErrorOr getRegUsedAsRetDest(const MCInst &Inst) const { llvm_unreachable("not implemented"); return getNoRegister(); @@ -622,6 +628,54 @@ class MCPlusBuilder { return std::make_pair(getNoRegister(), getNoRegister()); } + /// Analyzes if a pointer is checked to be authenticated successfully + /// by the end of the basic block. + /// + /// It is possible for pointer authentication instructions not to terminate + /// the program abnormally on authentication failure and return some invalid + /// pointer instead (like it is done on AArch64 when FEAT_FPAC is not + /// implemented). This might be enough to crash on invalid memory access when + /// the pointer is later used as the destination of a load, store, or branch + /// instruction. On the other hand, when the pointer is not used right away, + /// it may be important for the compiler to check the address explicitly not + /// to introduce a signing or authentication oracle. + /// + /// This function is intended to detect a complex, multi-instruction pointer- + /// checking sequence spanning a contiguous range of instructions at the end + /// of the basic block (as these sequences are expected to end with a + /// conditional branch - this is how they are implemented on AArch64 by LLVM). + /// If a (Reg, FirstInst) pair is returned and before execution of FirstInst + /// Reg was last written to by an authentication instruction, then it is known + /// that in any successor of BB either + /// * the authentication instruction that last wrote to Reg succeeded, or + /// * the program is terminated abnormally without introducing any signing + /// or authentication oracles + /// + /// Note that this function is not expected to repeat the results returned + /// by getAuthCheckedReg(Inst, MayOverwrite) function below. + virtual std::optional> + getAuthCheckedReg(BinaryBasicBlock &BB) const { + llvm_unreachable("not implemented"); + return std::nullopt; + } + + /// Returns the register that is checked to be authenticated successfully. + /// + /// If the returned register was last written to by an authentication + /// instruction and that authentication failed, then the program is known + /// to be terminated abnormally as a result of execution of Inst. + /// + /// Additionally, if MayOverwrite is false, it is known that the authenticated + /// pointer is not clobbered by Inst itself. + /// + /// Use this function for simple, single-instruction patterns instead of + /// its getAuthCheckedReg(BB) counterpart. + virtual MCPhysReg getAuthCheckedReg(const MCInst &Inst, + bool MayOverwrite) const { + llvm_unreachable("not implemented"); + return getNoRegister(); + } + virtual bool isTerminator(const MCInst &Inst) const; virtual bool isNoop(const MCInst &Inst) const { diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 08e0e914501de..12eb9c66130b9 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -173,10 +173,24 @@ class TrackedRegisters { /// X30 is safe-to-dereference - the state computed for sub- and /// super-registers is not inspected. struct SrcState { - /// A BitVector containing the registers that are either safe at function - /// entry and were not clobbered yet, or those not clobbered since being - /// authenticated. + /// A BitVector containing the registers that are either authenticated + /// (assuming failed authentication is permitted to produce an invalid + /// address, provided it generates an error on memory access) or whose + /// value is known not to be attacker-controlled under Pointer Authentication + /// threat model. The registers in this set are either + /// * not clobbered since being authenticated, or + /// * trusted at function entry and were not clobbered yet, or + /// * contain a safely materialized address. BitVector SafeToDerefRegs; + /// A BitVector containing the registers that are either authenticated + /// *successfully* or whose value is known not to be attacker-controlled + /// under Pointer Authentication threat model. + /// The registers in this set are either + /// * authenticated and then checked to be authenticated successfully + /// (and not clobbered since then), or + /// * trusted at function entry and were not clobbered yet, or + /// * contain a safely materialized address. + BitVector TrustedRegs; /// A vector of sets, only used in the second data flow run. /// Each element in the vector represents one of the registers for which we /// track the set of last instructions that wrote to this register. For @@ -189,7 +203,8 @@ struct SrcState { SrcState() {} SrcState(unsigned NumRegs, unsigned NumRegsToTrack) - : SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {} + : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs), + LastInstWritingReg(NumRegsToTrack) {} SrcState &merge(const SrcState &StateIn) { if (StateIn.empty()) @@ -198,6 +213,7 @@ struct SrcState { return (*this = StateIn); SafeToDerefRegs &= StateIn.SafeToDerefRegs; + TrustedRegs &= StateIn.TrustedRegs; for (unsigned I = 0; I < LastInstWritingReg.size(); ++I) for (const MCInst *J : StateIn.LastInstWritingReg[I]) LastInstWritingReg[I].insert(J); @@ -210,6 +226,7 @@ struct SrcState { bool operator==(const SrcState &RHS) const { return SafeToDerefRegs == RHS.SafeToDerefRegs && + TrustedRegs == RHS.TrustedRegs && LastInstWritingReg == RHS.LastInstWritingReg; } bool operator!=(const SrcState &RHS) const { return !((*this) == RHS); } @@ -234,6 +251,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SrcState &S) { OS << "empty"; } else { OS << "SafeToDerefRegs: " << S.SafeToDerefRegs << ", "; + OS << "TrustedRegs: " << S.TrustedRegs << ", "; printLastInsts(OS, S.LastInstWritingReg); } OS << ">"; @@ -254,18 +272,22 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const { OS << "src-state<"; if (S.empty()) { assert(S.SafeToDerefRegs.empty()); + assert(S.TrustedRegs.empty()); assert(S.LastInstWritingReg.empty()); OS << "empty"; } else { OS << "SafeToDerefRegs: "; RegStatePrinter.print(OS, S.SafeToDerefRegs); + OS << ", TrustedRegs: "; + RegStatePrinter.print(OS, S.TrustedRegs); OS << ", "; printLastInsts(OS, S.LastInstWritingReg); } OS << ">"; } -/// Computes which registers are safe to be used by control flow instructions. +/// Computes which registers are safe to be used by control flow and signing +/// instructions. /// /// This is the base class for two implementations: a dataflow-based analysis /// which is intended to be used for most functions and a simplified CFG-unaware @@ -293,6 +315,17 @@ class SrcSafetyAnalysis { /// RegToTrackInstsFor is the set of registers for which the dataflow analysis /// must compute which the last set of instructions writing to it are. const TrackedRegisters RegsToTrackInstsFor; + /// Stores information about the detected instruction sequences emitted to + /// check an authenticated pointer. Specifically, if such sequence is detected + /// in a basic block, it maps the last instruction of that basic block to + /// (CheckedRegister, FirstInstOfTheSequence) pair, see the description of + /// MCPlusBuilder::getAuthCheckedReg(BB) method. + /// + /// As the detection of such sequences requires iterating over the adjacent + /// instructions, it should be done before calling computeNext(), which + /// operates on separate instructions. + DenseMap> + CheckerSequenceInfo; SmallPtrSet &lastWritingInsts(SrcState &S, MCPhysReg Reg) const { @@ -308,7 +341,8 @@ class SrcSafetyAnalysis { SrcState createEntryState() { SrcState S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs()) - S.SafeToDerefRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true); + S.TrustedRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true); + S.SafeToDerefRegs = S.TrustedRegs; return S; } @@ -355,6 +389,47 @@ class SrcSafetyAnalysis { return Regs; } + // Returns all registers made trusted by this instruction. + SmallVector getRegsMadeTrusted(const MCInst &Point, + const SrcState &Cur) const { + SmallVector Regs; + const MCPhysReg NoReg = BC.MIB->getNoRegister(); + + // An authenticated pointer can be checked, or + MCPhysReg CheckedReg = + BC.MIB->getAuthCheckedReg(Point, /*MayOverwrite=*/false); + if (CheckedReg != NoReg && Cur.SafeToDerefRegs[CheckedReg]) + Regs.push_back(CheckedReg); + + if (CheckerSequenceInfo.contains(&Point)) { + MCPhysReg CheckedReg; + const MCInst *FirstCheckerInst; + std::tie(CheckedReg, FirstCheckerInst) = CheckerSequenceInfo.at(&Point); + + // FirstCheckerInst should belong to the same basic block (see the + // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was + // deterministically processed a few steps before this instruction. + const SrcState &StateBeforeChecker = + getStateBefore(*FirstCheckerInst).get(); + if (StateBeforeChecker.SafeToDerefRegs[CheckedReg]) + Regs.push_back(CheckedReg); + } + + // ... a safe address can be materialized, or + MCPhysReg NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point); + if (NewAddrReg != NoReg) + Regs.push_back(NewAddrReg); + + // ... an address can be updated in a safe manner, producing the result + // which is as trusted as the input address. + if (auto DstAndSrc = BC.MIB->analyzeAddressArithmeticsForPtrAuth(Point)) { + if (Cur.TrustedRegs[DstAndSrc->second]) + Regs.push_back(DstAndSrc->first); + } + + return Regs; + } + SrcState computeNext(const MCInst &Point, const SrcState &Cur) { SrcStatePrinter P(BC); LLVM_DEBUG({ @@ -381,11 +456,34 @@ class SrcSafetyAnalysis { BitVector Clobbered = getClobberedRegs(Point); SmallVector NewSafeToDerefRegs = getRegsMadeSafeToDeref(Point, Cur); + SmallVector NewTrustedRegs = getRegsMadeTrusted(Point, Cur); + + // Ideally, being trusted is a strictly stronger property than being + // safe-to-dereference. To simplify the computation of Next state, enforce + // this for NewSafeToDerefRegs and NewTrustedRegs. Additionally, this + // fixes the properly for "cumulative" register states in tricky cases + // like the following: + // + // ; LR is safe to dereference here + // mov x16, x30 ; start of the sequence, LR is s-t-d right before + // xpaclri ; clobbers LR, LR is not safe anymore + // cmp x30, x16 + // b.eq 1f ; end of the sequence: LR is marked as trusted + // brk 0x1234 + // 1: + // ; at this point LR would be marked as trusted, + // ; but not safe-to-dereference + // + for (auto TrustedReg : NewTrustedRegs) { + if (!is_contained(NewSafeToDerefRegs, TrustedReg)) + NewSafeToDerefRegs.push_back(TrustedReg); + } // Then, compute the state after this instruction is executed. SrcState Next = Cur; Next.SafeToDerefRegs.reset(Clobbered); + Next.TrustedRegs.reset(Clobbered); // Keep track of this instruction if it writes to any of the registers we // need to track that for: for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters()) @@ -406,6 +504,10 @@ class SrcSafetyAnalysis { lastWritingInsts(Next, Reg).clear(); } + // Process new trusted registers. + for (MCPhysReg TrustedReg : NewTrustedRegs) + Next.TrustedRegs |= BC.MIB->getAliases(TrustedReg, /*OnlySmaller=*/true); + LLVM_DEBUG({ dbgs() << " .. result: ("; P.print(dbgs(), Next); @@ -462,7 +564,26 @@ class DataflowSrcSafetyAnalysis return DFParent::getStateBefore(Inst); } - void run() override { DFParent::run(); } + void run() override { + for (BinaryBasicBlock &BB : Func) { + if (auto CheckerInfo = BC.MIB->getAuthCheckedReg(BB)) { + MCPhysReg CheckedReg = CheckerInfo->first; + MCInst &FirstInst = *CheckerInfo->second; + MCInst &LastInst = *BB.getLastNonPseudoInstr(); + LLVM_DEBUG({ + dbgs() << "Found pointer checking sequence in " << BB.getName() + << ":\n"; + traceReg(BC, "Checked register", CheckedReg); + traceInst(BC, "First instruction", FirstInst); + traceInst(BC, "Last instruction", LastInst); + }); + assert(llvm::any_of(BB, [&](MCInst &I) { return &I == &FirstInst; }) && + "Data-flow analysis expects the checker not to cross BBs"); + CheckerSequenceInfo[&LastInst] = *CheckerInfo; + } + } + DFParent::run(); + } protected: void preflight() {} @@ -658,6 +779,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst, return std::make_shared(CallKind, Inst, DestReg); } +static std::shared_ptr +shouldReportSigningOracle(const BinaryContext &BC, const MCInstReference &Inst, + const SrcState &S) { + static const GadgetKind SigningOracleKind("signing oracle found"); + + MCPhysReg SignedReg = BC.MIB->getSignedReg(Inst); + if (SignedReg == BC.MIB->getNoRegister()) + return nullptr; + + LLVM_DEBUG({ + traceInst(BC, "Found sign inst", Inst); + traceReg(BC, "Signed reg", SignedReg); + traceRegMask(BC, "TrustedRegs", S.TrustedRegs); + }); + if (S.TrustedRegs[SignedReg]) + return nullptr; + + return std::make_shared(SigningOracleKind, Inst, SignedReg); +} + template static void iterateOverInstrs(BinaryFunction &BF, T Fn) { if (BF.hasCFG()) { for (BinaryBasicBlock &BB : BF) @@ -702,6 +843,8 @@ Analysis::findGadgets(BinaryFunction &BF, if (auto Report = shouldReportCallGadget(BC, Inst, S)) Result.Diagnostics.push_back(Report); + if (auto Report = shouldReportSigningOracle(BC, Inst, S)) + Result.Diagnostics.push_back(Report); }); return Result; } diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index e00d6a18b0f6c..3c69dc9091231 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -257,6 +257,36 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { return AuthenticatedReg.getError() ? false : *AuthenticatedReg == Reg; } + MCPhysReg getSignedReg(const MCInst &Inst) const override { + switch (Inst.getOpcode()) { + case AArch64::PACIA: + case AArch64::PACIB: + case AArch64::PACDA: + case AArch64::PACDB: + case AArch64::PACIZA: + case AArch64::PACIZB: + case AArch64::PACDZA: + case AArch64::PACDZB: + return Inst.getOperand(0).getReg(); + case AArch64::PACIAZ: + case AArch64::PACIBZ: + case AArch64::PACIASP: + case AArch64::PACIBSP: + case AArch64::PACIASPPC: + case AArch64::PACIBSPPC: + case AArch64::PACNBIASPPC: + case AArch64::PACNBIBSPPC: + return AArch64::LR; + case AArch64::PACIA1716: + case AArch64::PACIB1716: + case AArch64::PACIA171615: + case AArch64::PACIB171615: + return AArch64::X17; + default: + return getNoRegister(); + } + } + ErrorOr getRegUsedAsRetDest(const MCInst &Inst) const override { assert(isReturn(Inst)); switch (Inst.getOpcode()) { @@ -339,6 +369,204 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { } } + std::optional> + getAuthCheckedReg(BinaryBasicBlock &BB) const override { + // Match several possible hard-coded sequences of instructions which can be + // emitted by LLVM backend to check that the authenticated pointer is + // correct (see AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue). + // + // This function only matches sequences involving branch instructions. + // All these sequences have the form: + // + // (0) ... regular code that authenticates a pointer in Xn ... + // (1) analyze Xn + // (2) branch to .Lon_success if the pointer is correct + // (3) BRK #imm (fall-through basic block) + // + // In the above pseudocode, (1) + (2) is one of the following sequences: + // + // - eor Xtmp, Xn, Xn, lsl #1 + // tbz Xtmp, #62, .Lon_success + // + // - mov Xtmp, Xn + // xpac(i|d) Xn (or xpaclri if Xn is LR) + // cmp Xtmp, Xn + // b.eq .Lon_success + // + // Note that any branch destination operand is accepted as .Lon_success - + // it is the responsibility of the caller of getAuthCheckedReg to inspect + // the list of successors of this basic block as appropriate. + + // Any of the above code sequences assume the fall-through basic block + // is a dead-end BRK instruction (any immediate operand is accepted). + const BinaryBasicBlock *BreakBB = BB.getFallthrough(); + if (!BreakBB || BreakBB->empty() || + BreakBB->front().getOpcode() != AArch64::BRK) + return std::nullopt; + + // Iterate over the instructions of BB in reverse order, matching opcodes + // and operands. + MCPhysReg TestedReg = 0; + MCPhysReg ScratchReg = 0; + auto It = BB.end(); + auto StepAndGetOpcode = [&It, &BB]() -> int { + if (It == BB.begin()) + return -1; + --It; + return It->getOpcode(); + }; + + switch (StepAndGetOpcode()) { + default: + // Not matched the branch instruction. + return std::nullopt; + case AArch64::Bcc: + // Bcc EQ, .Lon_success + if (It->getOperand(0).getImm() != AArch64CC::EQ) + return std::nullopt; + // Not checking .Lon_success (see above). + + // SUBSXrs XZR, TestedReg, ScratchReg, 0 (used by "CMP reg, reg" alias) + if (StepAndGetOpcode() != AArch64::SUBSXrs || + It->getOperand(0).getReg() != AArch64::XZR || + It->getOperand(3).getImm() != 0) + return std::nullopt; + TestedReg = It->getOperand(1).getReg(); + ScratchReg = It->getOperand(2).getReg(); + + // Either XPAC(I|D) ScratchReg, ScratchReg + // or XPACLRI + switch (StepAndGetOpcode()) { + default: + return std::nullopt; + case AArch64::XPACLRI: + // No operands to check, but using XPACLRI forces TestedReg to be X30. + if (TestedReg != AArch64::LR) + return std::nullopt; + break; + case AArch64::XPACI: + case AArch64::XPACD: + if (It->getOperand(0).getReg() != ScratchReg || + It->getOperand(1).getReg() != ScratchReg) + return std::nullopt; + break; + } + + // ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias) + if (StepAndGetOpcode() != AArch64::ORRXrs) + return std::nullopt; + if (It->getOperand(0).getReg() != ScratchReg || + It->getOperand(1).getReg() != AArch64::XZR || + It->getOperand(2).getReg() != TestedReg || + It->getOperand(3).getImm() != 0) + return std::nullopt; + + return std::make_pair(TestedReg, &*It); + + case AArch64::TBZX: + // TBZX ScratchReg, 62, .Lon_success + ScratchReg = It->getOperand(0).getReg(); + if (It->getOperand(1).getImm() != 62) + return std::nullopt; + // Not checking .Lon_success (see above). + + // EORXrs ScratchReg, TestedReg, TestedReg, 1 + if (StepAndGetOpcode() != AArch64::EORXrs) + return std::nullopt; + TestedReg = It->getOperand(1).getReg(); + if (It->getOperand(0).getReg() != ScratchReg || + It->getOperand(2).getReg() != TestedReg || + It->getOperand(3).getImm() != 1) + return std::nullopt; + + return std::make_pair(TestedReg, &*It); + } + } + + MCPhysReg getAuthCheckedReg(const MCInst &Inst, + bool MayOverwrite) const override { + // Cannot trivially reuse AArch64InstrInfo::getMemOperandWithOffsetWidth() + // method as it accepts an instance of MachineInstr, not MCInst. + const MCInstrDesc &Desc = Info->get(Inst.getOpcode()); + + // If signing oracles are considered, the particular value left in the base + // register after this instruction is important. This function checks that + // if the base register was overwritten, it is due to address write-back: + // + // ; good: + // autdza x1 ; x1 is authenticated (may fail) + // ldr x0, [x1, #8] ; x1 is checked and not changed + // pacdzb x1 + // + // ; also good: + // autdza x1 + // ldr x0, [x1, #8]! ; x1 is checked and incremented by 8 + // pacdzb x1 + // + // ; bad (the value being signed is not the authenticated one): + // autdza x1 + // ldr x1, [x1, #8] ; x1 is overwritten with an unrelated value + // pacdzb x1 + // + // ; also bad: + // autdza x1 + // pacdzb x1 ; possibly signing the result of failed authentication + // + // Note that this function is not needed for authentication oracles, as the + // particular value left in the register after a successful memory access + // is not important. + auto ClobbersBaseRegExceptWriteback = [&](unsigned BaseRegUseIndex) { + // FIXME: Compute the indices of address operands (base reg and written- + // back result) in AArch64InstrInfo instead of this ad-hoc code. + MCPhysReg BaseReg = Inst.getOperand(BaseRegUseIndex).getReg(); + unsigned WrittenBackDefIndex = Desc.getOperandConstraint( + BaseRegUseIndex, MCOI::OperandConstraint::TIED_TO); + + for (unsigned DefIndex = 0; DefIndex < Desc.getNumDefs(); ++DefIndex) { + // Address write-back is permitted: + // + // autda x0, x2 + // ; x0 is authenticated + // ldr x1, [x0, #8]! + // ; x0 is trusted (as authenticated and checked) + if (DefIndex == WrittenBackDefIndex) + continue; + + // Any other overwriting is not permitted: + // + // autda x0, x2 + // ; x0 is authenticated + // ldr w0, [x0] + // ; x0 is not authenticated anymore + if (RegInfo->regsOverlap(Inst.getOperand(DefIndex).getReg(), BaseReg)) + return true; + } + + return false; + }; + + if (mayLoad(Inst)) { + // The first Use operand is the base address register. + unsigned BaseRegIndex = Desc.getNumDefs(); + + // Reject non-immediate offsets, as adding a 64-bit register can change + // the resulting address arbitrarily. + for (unsigned I = BaseRegIndex + 1, E = Desc.getNumOperands(); I < E; ++I) + if (Inst.getOperand(I).isReg()) + return getNoRegister(); + + if (!MayOverwrite && ClobbersBaseRegExceptWriteback(BaseRegIndex)) + return getNoRegister(); + + return Inst.getOperand(BaseRegIndex).getReg(); + } + + // Store instructions are not handled yet, as they are not important for + // pauthtest ABI. Though, they could be handled similar to loads, if needed. + + return getNoRegister(); + } + bool isADRP(const MCInst &Inst) const override { return Inst.getOpcode() == AArch64::ADRP; } diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s b/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s new file mode 100644 index 0000000000000..3f982ddaf6e38 --- /dev/null +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s @@ -0,0 +1,664 @@ +// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe -Wl,--emit-relocs +// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s + + .text + + .globl raise_error + .type raise_error,@function +raise_error: + ret + .size raise_error, .-raise_error + + .globl resign_no_check + .type resign_no_check,@function +resign_no_check: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_no_check, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + pacia x0, x2 + ret + .size resign_no_check, .-resign_no_check + +// Test "xpac" check method. + + .globl resign_xpaci_good + .type resign_xpaci_good,@function +resign_xpaci_good: +// CHECK-NOT: resign_xpaci_good + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_good, .-resign_xpaci_good + + .globl resign_xpacd_good + .type resign_xpacd_good,@function +resign_xpacd_good: +// CHECK-NOT: resign_xpacd_good + autdb x0, x1 + mov x16, x0 + xpacd x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacda x0, x2 + ret + .size resign_xpacd_good, .-resign_xpacd_good + + .globl resign_xpaci_wrong_error + .type resign_xpaci_wrong_error,@function +resign_xpaci_wrong_error: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_error, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl raise_error + paciasp + stp x29, x30, [sp, #-16]! + + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + bl raise_error // should trigger breakpoint instead +1: + pacia x0, x2 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size resign_xpaci_wrong_error, .-resign_xpaci_wrong_error + + .globl resign_xpaci_missing_brk + .type resign_xpaci_missing_brk,@function +resign_xpaci_missing_brk: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_missing_brk, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x0, x16 + b.eq 1f +1: + pacia x0, x2 + ret + .size resign_xpaci_missing_brk, .-resign_xpaci_missing_brk + + .globl resign_xpaci_missing_branch_and_brk + .type resign_xpaci_missing_branch_and_brk,@function +resign_xpaci_missing_branch_and_brk: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_missing_branch_and_brk, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x0, x16 + pacia x0, x2 + ret + .size resign_xpaci_missing_branch_and_brk, .-resign_xpaci_missing_branch_and_brk + + .globl resign_xpaci_unrelated_auth_and_check + .type resign_xpaci_unrelated_auth_and_check,@function +resign_xpaci_unrelated_auth_and_check: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_unrelated_auth_and_check, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x10, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x10, x1 // made x10 safe-to-dereference + mov x16, x0 // start of checker sequence for x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x10, x2 + ret + .size resign_xpaci_unrelated_auth_and_check, .-resign_xpaci_unrelated_auth_and_check + +// There are lots of operands to check in the pattern - let's at the very least +// check that each of the three instructions (mov, xpac, cmp) undergoes *some* +// matching. Pay a bit more attention to those instructions and their operands +// that can be obviously replaced without crashing at run-time and making the +// check obviously weaker. + .globl resign_xpaci_wrong_pattern_1 + .type resign_xpaci_wrong_pattern_1,@function +resign_xpaci_wrong_pattern_1: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_1, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x10 // x10 instead of x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_wrong_pattern_1, .-resign_xpaci_wrong_pattern_1 + + .globl resign_xpaci_wrong_pattern_2 + .type resign_xpaci_wrong_pattern_2,@function +resign_xpaci_wrong_pattern_2: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_2, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: xpaci x0 + autib x0, x1 + mov x16, x0 + xpaci x0 // x0 instead of x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_wrong_pattern_2, .-resign_xpaci_wrong_pattern_2 + + .globl resign_xpaci_wrong_pattern_3 + .type resign_xpaci_wrong_pattern_3,@function +resign_xpaci_wrong_pattern_3: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_3, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x16, x16 // x16 instead of x0 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_wrong_pattern_3, .-resign_xpaci_wrong_pattern_3 + + .globl resign_xpaci_wrong_pattern_4 + .type resign_xpaci_wrong_pattern_4,@function +resign_xpaci_wrong_pattern_4: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_4, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x0 + xpaci x16 + cmp x0, x0 // x0 instead of x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_wrong_pattern_4, .-resign_xpaci_wrong_pattern_4 + + .globl resign_xpaci_wrong_pattern_5 + .type resign_xpaci_wrong_pattern_5,@function +resign_xpaci_wrong_pattern_5: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaci_wrong_pattern_5, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + mov x16, x0 + mov x16, x16 // replace xpaci with a no-op instruction + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_xpaci_wrong_pattern_5, .-resign_xpaci_wrong_pattern_5 + +// Test "xpac-hint" check method. + + .globl resign_xpaclri_good + .type resign_xpaclri_good,@function +resign_xpaclri_good: +// CHECK-NOT: resign_xpaclri_good + paciasp + stp x29, x30, [sp, #-16]! + + autib x30, x1 + mov x16, x30 + xpaclri + cmp x30, x16 + b.eq 1f + brk 0x1234 +1: + pacia x30, x2 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size resign_xpaclri_good, .-resign_xpaclri_good + + .globl xpaclri_check_keeps_lr_safe + .type xpaclri_check_keeps_lr_safe,@function +xpaclri_check_keeps_lr_safe: +// CHECK-NOT: xpaclri_check_keeps_lr_safe + // LR is implicitly safe-to-dereference and trusted here + mov x16, x30 + xpaclri // clobbers LR + cmp x30, x16 + b.eq 1f + brk 0x1234 // marks LR as trusted and safe-to-dereference +1: + ret // not reporting non-protected return + .size xpaclri_check_keeps_lr_safe, .-xpaclri_check_keeps_lr_safe + + .globl xpaclri_check_requires_safe_lr + .type xpaclri_check_requires_safe_lr,@function +xpaclri_check_requires_safe_lr: +// CHECK-LABEL: GS-PAUTH: non-protected ret found in function xpaclri_check_requires_safe_lr, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: xpaclri + mov x30, x0 + // LR is not safe-to-dereference here - check that xpac-hint checker + // does not make LR safe-to-dereference, but only *keeps* this state. + mov x16, x30 + xpaclri + cmp x30, x16 + b.eq 1f + brk 0x1234 +1: + ret + .size xpaclri_check_requires_safe_lr, .-xpaclri_check_requires_safe_lr + + .globl resign_xpaclri_wrong_reg + .type resign_xpaclri_wrong_reg,@function +resign_xpaclri_wrong_reg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_xpaclri_wrong_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x20, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + + autib x20, x1 // consistently using x20 instead of x30 + mov x16, x20 + xpaclri // ... but xpaclri still operates on x30 + cmp x20, x16 + b.eq 1f + brk 0x1234 +1: + pacia x20, x2 + + autiasp + ret + .size resign_xpaclri_wrong_reg, .-resign_xpaclri_wrong_reg + +// Test that pointer should be authenticated AND checked to be safe-to-sign. +// Checking alone is not enough. + .globl resign_checked_not_authenticated + .type resign_checked_not_authenticated,@function +resign_checked_not_authenticated: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_checked_not_authenticated, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + mov x16, x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_checked_not_authenticated, .-resign_checked_not_authenticated + +// The particular register should be *first* written by an authentication +// instruction and *then* that new value should be checked. +// Such code pattern will probably crash at run-time anyway, but let's check +// "safe-to-dereference" -> "trusted" transition. + .globl resign_checked_before_authenticated + .type resign_checked_before_authenticated,@function +resign_checked_before_authenticated: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_checked_before_authenticated, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + mov x16, x0 + xpaci x16 + cmp x0, x16 + b.eq 1f + brk 0x1234 +1: + autib x0, x1 + pacia x0, x2 + ret + .size resign_checked_before_authenticated, .-resign_checked_before_authenticated + +// Test "high-bits-notbi" check method. + + .globl resign_high_bits_tbz_good + .type resign_high_bits_tbz_good,@function +resign_high_bits_tbz_good: +// CHECK-NOT: resign_high_bits_tbz_good + autib x0, x1 + eor x16, x0, x0, lsl #1 + tbz x16, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_good, .-resign_high_bits_tbz_good + +// Check BRK matching briefly - this logic is shared with the "xpac" sequence matcher. + + .globl resign_high_bits_tbz_wrong_error + .type resign_high_bits_tbz_wrong_error,@function +resign_high_bits_tbz_wrong_error: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_error, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl raise_error + paciasp + stp x29, x30, [sp, #-16]! + + autib x0, x1 + eor x16, x0, x0, lsl #1 + tbz x16, #62, 1f + bl raise_error // should trigger breakpoint instead +1: + pacia x0, x2 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size resign_high_bits_tbz_wrong_error, .-resign_high_bits_tbz_wrong_error + + .globl resign_high_bits_tbz_wrong_bit + .type resign_high_bits_tbz_wrong_bit,@function +resign_high_bits_tbz_wrong_bit: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_bit, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x0, x0, lsl #1 + tbz x16, #63, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_bit, .-resign_high_bits_tbz_wrong_bit + + .globl resign_high_bits_tbz_wrong_shift_amount + .type resign_high_bits_tbz_wrong_shift_amount,@function +resign_high_bits_tbz_wrong_shift_amount: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_shift_amount, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x0, x0, lsl #2 + tbz x16, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_shift_amount, .-resign_high_bits_tbz_wrong_shift_amount + + .globl resign_high_bits_tbz_wrong_shift_type + .type resign_high_bits_tbz_wrong_shift_type,@function +resign_high_bits_tbz_wrong_shift_type: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_shift_type, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x0, x0, lsr #1 + tbz x16, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_shift_type, .-resign_high_bits_tbz_wrong_shift_type + + .globl resign_high_bits_tbz_wrong_pattern_1 + .type resign_high_bits_tbz_wrong_pattern_1,@function +resign_high_bits_tbz_wrong_pattern_1: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_1, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x0, x0, lsl #1 + tbz x17, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_pattern_1, .-resign_high_bits_tbz_wrong_pattern_1 + + .globl resign_high_bits_tbz_wrong_pattern_2 + .type resign_high_bits_tbz_wrong_pattern_2,@function +resign_high_bits_tbz_wrong_pattern_2: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_2, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x10, x0, lsl #1 + tbz x16, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_pattern_2, .-resign_high_bits_tbz_wrong_pattern_2 + + .globl resign_high_bits_tbz_wrong_pattern_3 + .type resign_high_bits_tbz_wrong_pattern_3,@function +resign_high_bits_tbz_wrong_pattern_3: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_high_bits_tbz_wrong_pattern_3, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autib x0, x1 + eor x16, x0, x10, lsl #1 + tbz x16, #62, 1f + brk 0x1234 +1: + pacia x0, x2 + ret + .size resign_high_bits_tbz_wrong_pattern_3, .-resign_high_bits_tbz_wrong_pattern_3 + +// Test checking by loading via the authenticated pointer. + + .globl resign_load_good + .type resign_load_good,@function +resign_load_good: +// CHECK-NOT: resign_load_good + autdb x0, x1 + ldr x3, [x0] + pacda x0, x2 + ret + .size resign_load_good, .-resign_load_good + + .globl resign_load_wreg_good + .type resign_load_wreg_good,@function +resign_load_wreg_good: +// CHECK-NOT: resign_load_wreg_good + autdb x0, x1 + ldr w3, [x0] + pacda x0, x2 + ret + .size resign_load_wreg_good, .-resign_load_wreg_good + + .globl resign_load_byte_good + .type resign_load_byte_good,@function +resign_load_byte_good: +// CHECK-NOT: resign_load_byte_good + autdb x0, x1 + ldrb w3, [x0] + pacda x0, x2 + ret + .size resign_load_byte_good, .-resign_load_byte_good + + .globl resign_load_pair_good + .type resign_load_pair_good,@function +resign_load_pair_good: +// CHECK-NOT: resign_load_pair_good + autdb x0, x1 + ldp x3, x4, [x0] + pacda x0, x2 + ret + .size resign_load_pair_good, .-resign_load_pair_good + + .globl resign_load_imm_offset_good + .type resign_load_imm_offset_good,@function +resign_load_imm_offset_good: +// CHECK-NOT: resign_load_imm_offset_good + autdb x0, x1 + ldr x3, [x0, #16] + pacda x0, x2 + ret + .size resign_load_imm_offset_good, .-resign_load_imm_offset_good + + .globl resign_load_preinc_good + .type resign_load_preinc_good,@function +resign_load_preinc_good: +// CHECK-NOT: resign_load_preinc_good + autdb x0, x1 + ldr x3, [x0, #16]! + pacda x0, x2 + ret + .size resign_load_preinc_good, .-resign_load_preinc_good + + .globl resign_load_postinc_good + .type resign_load_postinc_good,@function +resign_load_postinc_good: +// CHECK-NOT: resign_load_postinc_good + autdb x0, x1 + ldr x3, [x0], #16 + pacda x0, x2 + ret + .size resign_load_postinc_good, .-resign_load_postinc_good + + .globl resign_load_pair_with_ptr_writeback_good + .type resign_load_pair_with_ptr_writeback_good,@function +resign_load_pair_with_ptr_writeback_good: +// CHECK-NOT: resign_load_pair_with_ptr_writeback_good + autdb x0, x1 + ldp x3, x4, [x0, #16]! // three output registers (incl. tied x0 register) + pacda x0, x2 + ret + .size resign_load_pair_with_ptr_writeback_good, .-resign_load_pair_with_ptr_writeback_good + + .globl resign_load_overwrite + .type resign_load_overwrite,@function +resign_load_overwrite: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_overwrite, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0] + autdb x0, x1 + ldr x0, [x0] + pacda x0, x2 + ret + .size resign_load_overwrite, .-resign_load_overwrite + + .globl resign_load_overwrite_out2 + .type resign_load_overwrite_out2,@function +resign_load_overwrite_out2: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_overwrite_out2, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x10, x0, [x0] + autdb x0, x1 + ldp x10, x0, [x0] + pacda x0, x2 + ret + .size resign_load_overwrite_out2, .-resign_load_overwrite_out2 + + .globl resign_load_partial_overwrite + .type resign_load_partial_overwrite,@function +resign_load_partial_overwrite: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_partial_overwrite, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr w0, [x0] + autdb x0, x1 + ldr w0, [x0] + pacda x0, x2 + ret + .size resign_load_partial_overwrite, .-resign_load_partial_overwrite + + .globl resign_load_partial_overwrite_out2 + .type resign_load_partial_overwrite_out2,@function +resign_load_partial_overwrite_out2: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_partial_overwrite_out2, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp w10, w0, [x0] + autdb x0, x1 + ldp w10, w0, [x0] + pacda x0, x2 + ret + .size resign_load_partial_overwrite_out2, .-resign_load_partial_overwrite_out2 + +// Test that base register + offset register addressing mode is rejected. + + .globl resign_load_reg_plus_reg + .type resign_load_reg_plus_reg,@function +resign_load_reg_plus_reg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_reg_plus_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autdb x0, x1 + ldr x3, [x0, x4] + pacda x0, x2 + ret + .size resign_load_reg_plus_reg, .-resign_load_reg_plus_reg + + .globl resign_load_reg_plus_reg_in2 + .type resign_load_reg_plus_reg_in2,@function +resign_load_reg_plus_reg_in2: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function resign_load_reg_plus_reg_in2, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x2 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autdb x0, x1 + ldr x3, [x4, x0] + pacda x0, x2 + ret + .size resign_load_reg_plus_reg_in2, .-resign_load_reg_plus_reg_in2 + + .globl resign_load_unscaled_good + .type resign_load_unscaled_good,@function +resign_load_unscaled_good: +// CHECK-NOT: resign_load_unscaled_good + autdb x0, x1 + ldurb w3, [x0, #-1] + pacda x0, x2 + ret + .size resign_load_unscaled_good, .-resign_load_unscaled_good + +// Any basic block can check at most one register using a multi-instruction +// pointer-checking sequence, but it can contain an arbitrary number of single- +// instruction pointer checks. + + .globl many_checked_regs + .type many_checked_regs,@function +many_checked_regs: +// CHECK-NOT: many_checked_regs + autdzb x0 + autdzb x1 + autdzb x2 + b 1f +1: + ldr w3, [x0] // single-instruction check + ldr w3, [x1] // single-instruction check + mov x16, x2 // start of multi-instruction checker sequence + xpacd x16 // ... + cmp x2, x16 // ... + b.eq 2f // end of basic block + brk 0x1234 +2: + pacdza x0 + pacdza x1 + pacdza x2 + ret + .size many_checked_regs, .-many_checked_regs + + .globl main + .type main,@function +main: + mov x0, 0 + ret + .size main, .-main diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-address-materialization.s b/bolt/test/binary-analysis/AArch64/gs-pauth-address-materialization.s index b4dd53a5e3c8d..6648f96ad7d08 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-address-materialization.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-address-materialization.s @@ -8,13 +8,6 @@ // Note that while "instructions that write to the affected registers" // section of the report is still technically correct, it does not necessarily // mention the instructions that are used incorrectly. -// -// FIXME: Switch to PAC* instructions instead of indirect tail call for testing -// if a register is considered safe when detection of signing oracles is -// implemented, as it is more traditional usage of PC-relative constants. -// Moreover, using PAC instructions would improve test robustness, as -// handling of *calls* can be influenced by what BOLT classifies as a -// tail call, for example. .text @@ -29,7 +22,8 @@ sym: good_adr: // CHECK-NOT: good_adr adr x0, sym - br x0 + paciza x0 + ret .size good_adr, .-good_adr .globl good_adrp @@ -37,7 +31,8 @@ good_adr: good_adrp: // CHECK-NOT: good_adrp adrp x0, sym - br x0 + paciza x0 + ret .size good_adrp, .-good_adrp .globl good_adrp_add @@ -46,7 +41,8 @@ good_adrp_add: // CHECK-NOT: good_adrp_add adrp x0, sym add x0, x0, :lo12:sym - br x0 + paciza x0 + ret .size good_adrp_add, .-good_adrp_add .globl good_adrp_add_with_const_offset @@ -56,40 +52,45 @@ good_adrp_add_with_const_offset: adrp x0, sym add x0, x0, :lo12:sym add x0, x0, #8 - br x0 + paciza x0 + ret .size good_adrp_add_with_const_offset, .-good_adrp_add_with_const_offset .globl bad_adrp_with_nonconst_offset .type bad_adrp_with_nonconst_offset,@function bad_adrp_with_nonconst_offset: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_adrp_with_nonconst_offset, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_adrp_with_nonconst_offset, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1 // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{.*}} // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1 -// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ret adrp x0, sym add x0, x0, x1 - br x0 + paciza x0 + ret .size bad_adrp_with_nonconst_offset, .-bad_adrp_with_nonconst_offset .globl bad_split_adrp .type bad_split_adrp,@function bad_split_adrp: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_split_adrp, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_split_adrp, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}} // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}} -// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ret cbz x2, 1f adrp x0, sym 1: add x0, x0, :lo12:sym - br x0 + paciza x0 + ret .size bad_split_adrp, .-bad_split_adrp // Materialization of absolute addresses is not handled, as it is not expected @@ -98,15 +99,17 @@ bad_split_adrp: .globl bad_immediate_constant .type bad_immediate_constant,@function bad_immediate_constant: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_immediate_constant, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_immediate_constant, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, #{{.*}} // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: mov x0, #{{.*}} -// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ret movz x0, #1234 - br x0 + paciza x0 + ret .size bad_immediate_constant, .-bad_immediate_constant // Any ADR or ADRP instruction followed by any number of increments/decrements @@ -118,7 +121,8 @@ good_adr_with_add: // CHECK-NOT: good_adr_with_add adr x0, sym add x0, x0, :lo12:sym - br x0 + paciza x0 + ret .size good_adr_with_add, .-good_adr_with_add .globl good_adrp_with_add_non_consecutive @@ -128,7 +132,8 @@ good_adrp_with_add_non_consecutive: adrp x0, sym mul x1, x2, x3 add x0, x0, :lo12:sym - br x0 + paciza x0 + ret .size good_adrp_with_add_non_consecutive, .-good_adrp_with_add_non_consecutive .globl good_many_offsets @@ -138,7 +143,8 @@ good_many_offsets: adrp x0, sym add x1, x0, #8 add x2, x1, :lo12:sym - br x2 + paciza x2 + ret .size good_many_offsets, .-good_many_offsets .globl good_negative_offset @@ -147,7 +153,8 @@ good_negative_offset: // CHECK-NOT: good_negative_offset adr x0, sym sub x1, x0, #8 - br x1 + paciza x1 + ret .size good_negative_offset, .-good_negative_offset // MOV Xd, Xm (which is an alias of ORR Xd, XZR, Xm) is handled as part of @@ -161,45 +168,50 @@ good_mov_reg: adrp x0, sym mov x1, x0 orr x2, xzr, x1 // the same as "mov x2, x1" - br x2 + paciza x2 + ret .size good_mov_reg, .-good_mov_reg .globl bad_orr_not_xzr .type bad_orr_not_xzr,@function bad_orr_not_xzr: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_orr_not_xzr, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x2 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_xzr, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, x1, x0 // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} // CHECK-NEXT: {{[0-9a-f]+}}: mov x1, #0 // CHECK-NEXT: {{[0-9a-f]+}}: orr x2, x1, x0 -// CHECK-NEXT: {{[0-9a-f]+}}: br x2 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x2 +// CHECK-NEXT: {{[0-9a-f]+}}: ret adrp x0, sym // The generic case of "orr Xd, Xn, Xm" is not allowed so far, // even if Xn is known to be safe movz x1, #0 orr x2, x1, x0 - br x2 + paciza x2 + ret .size bad_orr_not_xzr, .-bad_orr_not_xzr .globl bad_orr_not_lsl0 .type bad_orr_not_lsl0,@function bad_orr_not_lsl0: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_orr_not_lsl0, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x2 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_lsl0, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1 // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} // CHECK-NEXT: {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1 -// CHECK-NEXT: {{[0-9a-f]+}}: br x2 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x2 +// CHECK-NEXT: {{[0-9a-f]+}}: ret adrp x0, sym // Currently, the only allowed form of "orr" is that used by "mov Xd, Xn" alias. // This can be relaxed in the future. orr x2, xzr, x0, lsl #1 - br x2 + paciza x2 + ret .size bad_orr_not_lsl0, .-bad_orr_not_lsl0 // Check that the input register operands of `add`/`mov` is correct. @@ -207,33 +219,37 @@ bad_orr_not_lsl0: .globl bad_add_input_reg .type bad_add_input_reg,@function bad_add_input_reg: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_add_input_reg, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_add_input_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}} // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}} -// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ret adrp x0, sym add x0, x1, :lo12:sym - br x0 + paciza x0 + ret .size bad_add_input_reg, .-bad_add_input_reg .globl bad_mov_input_reg .type bad_mov_input_reg,@function bad_mov_input_reg: -// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_mov_input_reg, basic block {{[^,]+}}, at address -// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_mov_input_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: // CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, x1 // CHECK-NEXT: This happens in the following basic block: // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} // CHECK-NEXT: {{[0-9a-f]+}}: mov x0, x1 -// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL +// CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ret adrp x0, sym mov x0, x1 - br x0 + paciza x0 + ret .size bad_mov_input_reg, .-bad_mov_input_reg .globl main diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s index 0b6bd5509a42a..c79c5926a05cd 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s @@ -591,7 +591,9 @@ obscure_indirect_call_arg_nocfg: .globl safe_lr_at_function_entry_nocfg .type safe_lr_at_function_entry_nocfg,@function safe_lr_at_function_entry_nocfg: -// CHECK-NOT: safe_lr_at_function_entry_nocfg +// Due to state being reset after a label, paciasp is reported as +// a signing oracle - this is a known false positive, ignore it. +// CHECK-NOT: non-protected call{{.*}}safe_lr_at_function_entry_nocfg cbz x0, 1f ret // LR is safe at the start of the function 1: diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s index e4a74bfe1e17a..e0e8471c36a7e 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s @@ -51,40 +51,40 @@ simple: // CHECK-NEXT: End of Function "simple" // CHECK-EMPTY: // CHECK-NEXT: Running src register safety analysis... -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #25, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #25, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence( // CHECK-NEXT: State 1: src-state -// CHECK-NEXT: State 2: src-state) -// CHECK-NEXT: merged state: src-state -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: State 2: src-state) +// CHECK-NEXT: merged state: src-state +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence( -// CHECK-NEXT: State 1: src-state -// CHECK-NEXT: State 2: src-state) -// CHECK-NEXT: merged state: src-state -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: State 1: src-state +// CHECK-NEXT: State 2: src-state) +// CHECK-NEXT: merged state: src-state +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( autiza x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( blr x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( hint #29, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: After src register safety analysis: // CHECK-NEXT: Binary Function "simple" { // CHECK-NEXT: Number : 1 @@ -94,27 +94,30 @@ simple: // CHECK-NEXT: } // CHECK-NEXT: [[BB0]] (3 instructions, align : 1) // CHECK-NEXT: Entry Point -// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: Successors: [[BB1]] // CHECK-EMPTY: // CHECK-NEXT: [[BB1]] (5 instructions, align : 1) // CHECK-NEXT: Predecessors: [[BB0]] -// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state -// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-EMPTY: // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: // CHECK-NEXT: End of Function "simple" // CHECK-EMPTY: -// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state +// PAUTH-NEXT: Found sign inst: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state +// PAUTH-NEXT: Signed reg: LR +// PAUTH-NEXT: TrustedRegs: LR W30 W30_HI +// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state // PAUTH-NEXT: Call destination reg: X0 // PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}} -// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none) // CHECK-NEXT: SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}} @@ -129,10 +132,10 @@ clobber: // CHECK-LABEL:Analyzing in function clobber, AllocatorId 1 // ... // CHECK: Running src register safety analysis... -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: After src register safety analysis: // CHECK-NEXT: Binary Function "clobber" { // ... @@ -141,16 +144,16 @@ clobber: // The above output was printed after first run of analysis // CHECK-EMPTY: -// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none) // CHECK-NEXT: SafeToDerefRegs: W30_HI{{[ \t]*$}} // CHECK-EMPTY: // CHECK-NEXT: Running detailed src register safety analysis... -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( mov w30, #0x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: After detailed src register safety analysis: // CHECK-NEXT: Binary Function "clobber" { // ... @@ -160,7 +163,7 @@ clobber: // Iterating over the reports and attaching clobbering info: // CHECK-EMPTY: -// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state .globl nocfg .type nocfg,@function @@ -193,13 +196,13 @@ nocfg: // CHECK-NEXT: End of Function "nocfg" // CHECK-EMPTY: // CHECK-NEXT: Running src register safety analysis... -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8 -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: After src register safety analysis: // CHECK-NEXT: Binary Function "nocfg" { // CHECK-NEXT: Number : 3 @@ -208,31 +211,31 @@ nocfg: // CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]] // CHECK-NEXT: } // CHECK-NEXT: .{{[A-Za-z0-9]+}}: -// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point): // CHECK-NEXT: .{{[A-Za-z0-9]+}}: -// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: // CHECK-NEXT: End of Function "nocfg" // CHECK-EMPTY: -// PAUTH-NEXT: Found call inst: 00000000: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// PAUTH-NEXT: Found call inst: 00000000: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state // PAUTH-NEXT: Call destination reg: X0 // PAUTH-NEXT: SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI -// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none) // CHECK-NEXT: SafeToDerefRegs: // CHECK-EMPTY: // CHECK-NEXT: Running detailed src register safety analysis... -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) -// CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8 -// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) -// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: After detailed src register safety analysis: // CHECK-NEXT: Binary Function "nocfg" { // CHECK-NEXT: Number : 3 @@ -240,16 +243,16 @@ nocfg: // CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]] // CHECK-NEXT: } // CHECK-NEXT: .{{[A-Za-z0-9]+}}: -// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point): // CHECK-NEXT: .{{[A-Za-z0-9]+}}: -// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: // CHECK-NEXT: End of Function "nocfg" // CHECK-EMPTY: -// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-LABEL:Analyzing in function main, AllocatorId 1 .globl main diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s new file mode 100644 index 0000000000000..10fcf08cf158c --- /dev/null +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s @@ -0,0 +1,993 @@ +// RUN: %clang %cflags -march=armv8.3-a+pauth-lr -Wl,--no-relax %s -o %t.exe +// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck -check-prefix=PACRET %s +// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s + +// The detection of compiler-generated explicit pointer checks is tested in +// gs-pauth-address-checks.s, for that reason only test here "dummy-load" and +// "high-bits-notbi" checkers, as the shortest examples of checkers that are +// detected per-instruction and per-BB. + +// PACRET-NOT: signing oracle found in function + + .text + + .type sym,@function +sym: + ret + .size sym, .-sym + + .globl callee + .type callee,@function +callee: + ret + .size callee, .-callee + +// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted: +// * trusted right away: safe address materialization +// * trusted as checked s-t-d: two variants of checks +// * untrusted: s-t-d, but not checked +// * untrusted: not s-t-d, but checked +// * untrusted: not even s-t-d - from arg and from memory +// * untrusted: {subreg clobbered, function called} X {between address materialization and use, between auth and check, between check and use} +// * untrusted: first checked then auted, auted then auted, checked then checked + + .globl good_sign_addr_mat + .type good_sign_addr_mat,@function +good_sign_addr_mat: +// CHECK-NOT: good_sign_addr_mat + adr x0, sym + pacda x0, x1 + ret + .size good_sign_addr_mat, .-good_sign_addr_mat + + .globl good_sign_auted_checked_ldr + .type good_sign_auted_checked_ldr,@function +good_sign_auted_checked_ldr: +// CHECK-NOT: good_sign_auted_checked_ldr + autda x0, x2 + ldr x2, [x0] + pacda x0, x1 + ret + .size good_sign_auted_checked_ldr, .-good_sign_auted_checked_ldr + + .globl good_sign_auted_checked_brk + .type good_sign_auted_checked_brk,@function +good_sign_auted_checked_brk: +// CHECK-NOT: good_sign_auted_checked_brk + autda x0, x2 + eor x16, x0, x0, lsl #1 + tbz x16, #62, 1f + brk 0x1234 +1: + pacda x0, x1 + ret + .size good_sign_auted_checked_brk, .-good_sign_auted_checked_brk + + .globl bad_sign_authed_unchecked + .type bad_sign_authed_unchecked,@function +bad_sign_authed_unchecked: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autda x0, x2 + pacda x0, x1 + ret + .size bad_sign_authed_unchecked, .-bad_sign_authed_unchecked + + .globl bad_sign_checked_not_auted + .type bad_sign_checked_not_auted,@function +bad_sign_checked_not_auted: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_sign_checked_not_auted, .-bad_sign_checked_not_auted + + .globl bad_sign_plain_arg + .type bad_sign_plain_arg,@function +bad_sign_plain_arg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + pacda x0, x1 + ret + .size bad_sign_plain_arg, .-bad_sign_plain_arg + + .globl bad_sign_plain_mem + .type bad_sign_plain_mem,@function +bad_sign_plain_mem: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1] +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x1] +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + ldr x0, [x1] + pacda x0, x1 + ret + .size bad_sign_plain_mem, .-bad_sign_plain_mem + + .globl bad_clobber_between_addr_mat_and_use + .type bad_clobber_between_addr_mat_and_use,@function +bad_clobber_between_addr_mat_and_use: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: adr x0, "sym/1" +// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + adr x0, sym + mov w0, w3 + pacda x0, x1 + ret + .size bad_clobber_between_addr_mat_and_use, .-bad_clobber_between_addr_mat_and_use + + .globl bad_clobber_between_auted_and_checked + .type bad_clobber_between_auted_and_checked,@function +bad_clobber_between_auted_and_checked: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + autda x0, x2 + mov w0, w3 + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_clobber_between_auted_and_checked, .-bad_clobber_between_auted_and_checked + + .globl bad_clobber_between_checked_and_used + .type bad_clobber_between_checked_and_used,@function +bad_clobber_between_checked_and_used: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w3 +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + autda x0, x2 + ldr x2, [x0] + mov w0, w3 + pacda x0, x1 + ret + .size bad_clobber_between_checked_and_used, .-bad_clobber_between_checked_and_used + + .globl bad_call_between_addr_mat_and_use + .type bad_call_between_addr_mat_and_use,@function +bad_call_between_addr_mat_and_use: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_addr_mat_and_use, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: paciasp +// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]! +// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: adr x0, "sym/1" +// CHECK-NEXT: {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + adr x0, sym + bl callee + pacda x0, x1 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_call_between_addr_mat_and_use, .-bad_call_between_addr_mat_and_use + + .globl bad_call_between_auted_and_checked + .type bad_call_between_auted_and_checked,@function +bad_call_between_auted_and_checked: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_auted_and_checked, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: paciasp +// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]! +// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autda x0, x2 + bl callee + ldr x2, [x0] + pacda x0, x1 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_call_between_auted_and_checked, .-bad_call_between_auted_and_checked + + .globl bad_call_between_checked_and_used + .type bad_call_between_checked_and_used,@function +bad_call_between_checked_and_used: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_call_between_checked_and_used, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: paciasp +// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]! +// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: bl callee +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autda x0, x2 + ldr x2, [x0] + bl callee + pacda x0, x1 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_call_between_checked_and_used, .-bad_call_between_checked_and_used + + .globl bad_transition_check_then_auth + .type bad_transition_check_then_auth,@function +bad_transition_check_then_auth: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_auth, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + ldr x2, [x0] + autda x0, x2 + pacda x0, x1 + ret + .size bad_transition_check_then_auth, .-bad_transition_check_then_auth + + .globl bad_transition_auth_then_auth + .type bad_transition_auth_then_auth,@function +bad_transition_auth_then_auth: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_auth_then_auth, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autda x0, x2 + autda x0, x2 + pacda x0, x1 + ret + .size bad_transition_auth_then_auth, .-bad_transition_auth_then_auth + + .globl bad_transition_check_then_check + .type bad_transition_check_then_check,@function +bad_transition_check_then_check: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_check, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + ldr x2, [x0] + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_transition_check_then_check, .-bad_transition_check_then_check + +// Multi-BB test cases. + +// Test state propagation across multiple basic blocks. +// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted: +// * trusted right away: safe address materialization +// * trusted as checked s-t-d: two variants of checks +// * untrusted: s-t-d, but not *always* checked +// * untrusted: not *always* s-t-d, but checked +// * untrusted: not even s-t-d - from arg and from memory +// * untrusted: subreg clobbered - between address materialization and use, between auth and check, between check and use +// * trusted in both predecessors but for different reasons +// (the one due to address materialization and the other due to s-t-d then checked) +// * untrusted: auted in one predecessor, checked in the other + + .globl good_sign_addr_mat_multi_bb + .type good_sign_addr_mat_multi_bb,@function +good_sign_addr_mat_multi_bb: +// CHECK-NOT: good_sign_addr_mat_multi_bb + adr x0, sym + cbz x3, 1f + nop +1: + pacda x0, x1 + ret + .size good_sign_addr_mat_multi_bb, .-good_sign_addr_mat_multi_bb + + .globl good_sign_auted_checked_ldr_multi_bb + .type good_sign_auted_checked_ldr_multi_bb,@function +good_sign_auted_checked_ldr_multi_bb: +// CHECK-NOT: good_sign_auted_checked_ldr_multi_bb + autda x0, x2 + cbz x3, 1f + nop +1: + ldr x2, [x0] + cbz x4, 2f + nop +2: + pacda x0, x1 + ret + .size good_sign_auted_checked_ldr_multi_bb, .-good_sign_auted_checked_ldr_multi_bb + + .globl good_sign_auted_checked_brk_multi_bb + .type good_sign_auted_checked_brk_multi_bb,@function +good_sign_auted_checked_brk_multi_bb: +// CHECK-NOT: good_sign_auted_checked_brk_multi_bb + autda x0, x2 + cbz x3, 1f + nop +1: + eor x16, x0, x0, lsl #1 + tbz x16, #62, 2f + brk 0x1234 +2: + cbz x4, 3f + nop +3: + pacda x0, x1 + ret + .size good_sign_auted_checked_brk_multi_bb, .-good_sign_auted_checked_brk_multi_bb + + .globl bad_sign_authed_unchecked_multi_bb + .type bad_sign_authed_unchecked_multi_bb,@function +bad_sign_authed_unchecked_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + autda x0, x2 + cbz x3, 1f + ldr x2, [x0] +1: + pacda x0, x1 + ret + .size bad_sign_authed_unchecked_multi_bb, .-bad_sign_authed_unchecked_multi_bb + + .globl bad_sign_checked_not_auted_multi_bb + .type bad_sign_checked_not_auted_multi_bb,@function +bad_sign_checked_not_auted_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + cbz x3, 1f + autda x0, x2 +1: + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_sign_checked_not_auted_multi_bb, .-bad_sign_checked_not_auted_multi_bb + + .globl bad_sign_plain_arg_multi_bb + .type bad_sign_plain_arg_multi_bb,@function +bad_sign_plain_arg_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + cbz x3, 1f + autda x0, x2 + ldr x2, [x0] +1: + pacda x0, x1 + ret + .size bad_sign_plain_arg_multi_bb, .-bad_sign_plain_arg_multi_bb + + .globl bad_sign_plain_mem_multi_bb + .type bad_sign_plain_mem_multi_bb,@function +bad_sign_plain_mem_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1] + ldr x0, [x1] + cbz x3, 1f + autda x0, x2 + ldr x2, [x0] +1: + pacda x0, x1 + ret + .size bad_sign_plain_mem_multi_bb, .-bad_sign_plain_mem_multi_bb + + .globl bad_clobber_between_addr_mat_and_use_multi_bb + .type bad_clobber_between_addr_mat_and_use_multi_bb,@function +bad_clobber_between_addr_mat_and_use_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 + adr x0, sym + cbz x4, 1f + mov w0, w3 +1: + pacda x0, x1 + ret + .size bad_clobber_between_addr_mat_and_use_multi_bb, .-bad_clobber_between_addr_mat_and_use_multi_bb + + .globl bad_clobber_between_auted_and_checked_multi_bb + .type bad_clobber_between_auted_and_checked_multi_bb,@function +bad_clobber_between_auted_and_checked_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 + autda x0, x2 + cbz x4, 1f + mov w0, w3 +1: + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_clobber_between_auted_and_checked_multi_bb, .-bad_clobber_between_auted_and_checked_multi_bb + + .globl bad_clobber_between_checked_and_used_multi_bb + .type bad_clobber_between_checked_and_used_multi_bb,@function +bad_clobber_between_checked_and_used_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 + autda x0, x2 + ldr x2, [x0] + cbz x4, 1f + mov w0, w3 +1: + pacda x0, x1 + ret + .size bad_clobber_between_checked_and_used_multi_bb, .-bad_clobber_between_checked_and_used_multi_bb + + .globl good_both_trusted_multi_bb + .type good_both_trusted_multi_bb,@function +good_both_trusted_multi_bb: +// CHECK-NOT: good_both_trusted_multi_bb + cbz x2, 1f + autdb x0, x1 + ldr x2, [x0] + b 2f +1: + adr x0, sym +2: + pacda x0, x1 + ret + .size good_both_trusted_multi_bb, .-good_both_trusted_multi_bb + + .globl bad_one_auted_one_checked_multi_bb + .type bad_one_auted_one_checked_multi_bb,@function +bad_one_auted_one_checked_multi_bb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_one_auted_one_checked_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + cbz x2, 1f + autdb x0, x1 + b 2f +1: + ldr x3, [x0] +2: + pacda x0, x1 + ret + .size bad_one_auted_one_checked_multi_bb, .-bad_one_auted_one_checked_multi_bb + +// Test the detection when no CFG was reconstructed for a function. +// Test transitions between register states: none, safe-to-dereference (s-t-d), trusted: +// * trusted right away: safe address materialization +// * trusted as checked s-t-d: only check by load (FIXME: support BRK-based code sequences) +// * untrusted: s-t-d, but not checked +// * untrusted: not s-t-d, but checked +// * untrusted: not even s-t-d - from arg and from memory +// * untrusted: subreg clobbered - between address materialization and use, between auth and check, between check and use +// * untrusted: first checked then auted, auted then auted, checked then checked +// +// Note that it is important to sign and authenticate LR, as it is not kept +// safe-to-dereference across unconditional branches. + + .globl good_sign_addr_mat_nocfg + .type good_sign_addr_mat_nocfg,@function +good_sign_addr_mat_nocfg: +// CHECK-NOT: good_sign_addr_mat_nocfg + paciasp + adr x3, 1f + br x3 +1: + adr x0, sym + pacda x0, x1 + autiasp + ret + .size good_sign_addr_mat_nocfg, .-good_sign_addr_mat_nocfg + + .globl good_sign_auted_checked_ldr_nocfg + .type good_sign_auted_checked_ldr_nocfg,@function +good_sign_auted_checked_ldr_nocfg: +// CHECK-NOT: good_sign_auted_checked_ldr_nocfg + paciasp + adr x3, 1f + br x3 +1: + autda x0, x2 + ldr x2, [x0] + pacda x0, x1 + autiasp + ret + .size good_sign_auted_checked_ldr_nocfg, .-good_sign_auted_checked_ldr_nocfg + + .globl bad_sign_authed_unchecked_nocfg + .type bad_sign_authed_unchecked_nocfg,@function +bad_sign_authed_unchecked_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_authed_unchecked_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + autda x0, x2 + pacda x0, x1 + autiasp + ret + .size bad_sign_authed_unchecked_nocfg, .-bad_sign_authed_unchecked_nocfg + + .globl bad_sign_checked_not_auted_nocfg + .type bad_sign_checked_not_auted_nocfg,@function +bad_sign_checked_not_auted_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_checked_not_auted_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + ldr x2, [x0] + pacda x0, x1 + autiasp + ret + .size bad_sign_checked_not_auted_nocfg, .-bad_sign_checked_not_auted_nocfg + + .globl bad_sign_plain_arg_nocfg + .type bad_sign_plain_arg_nocfg,@function +bad_sign_plain_arg_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_arg_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + pacda x0, x1 + autiasp + ret + .size bad_sign_plain_arg_nocfg, .-bad_sign_plain_arg_nocfg + + .globl bad_sign_plain_mem_nocfg + .type bad_sign_plain_mem_nocfg,@function +bad_sign_plain_mem_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_sign_plain_mem_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x1] + paciasp + adr x3, 1f + br x3 +1: + ldr x0, [x1] + pacda x0, x1 + autiasp + ret + .size bad_sign_plain_mem_nocfg, .-bad_sign_plain_mem_nocfg + + .globl bad_clobber_between_addr_mat_and_use_nocfg + .type bad_clobber_between_addr_mat_and_use_nocfg,@function +bad_clobber_between_addr_mat_and_use_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_addr_mat_and_use_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4 + paciasp + adr x3, 1f + br x3 +1: + adr x0, sym + mov w0, w4 + pacda x0, x1 + autiasp + ret + .size bad_clobber_between_addr_mat_and_use_nocfg, .-bad_clobber_between_addr_mat_and_use_nocfg + + .globl bad_clobber_between_auted_and_checked_nocfg + .type bad_clobber_between_auted_and_checked_nocfg,@function +bad_clobber_between_auted_and_checked_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_auted_and_checked_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4 + paciasp + adr x3, 1f + br x3 +1: + autda x0, x2 + mov w0, w4 + ldr x2, [x0] + pacda x0, x1 + autiasp + ret + .size bad_clobber_between_auted_and_checked_nocfg, .-bad_clobber_between_auted_and_checked_nocfg + + .globl bad_clobber_between_checked_and_used_nocfg + .type bad_clobber_between_checked_and_used_nocfg,@function +bad_clobber_between_checked_and_used_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_clobber_between_checked_and_used_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w4 + paciasp + adr x3, 1f + br x3 +1: + autda x0, x2 + ldr x2, [x0] + mov w0, w4 + pacda x0, x1 + autiasp + ret + .size bad_clobber_between_checked_and_used_nocfg, .-bad_clobber_between_checked_and_used_nocfg + + .globl bad_transition_check_then_auth_nocfg + .type bad_transition_check_then_auth_nocfg,@function +bad_transition_check_then_auth_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_auth_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + ldr x2, [x0] + autda x0, x2 + pacda x0, x1 + autiasp + ret + .size bad_transition_check_then_auth_nocfg, .-bad_transition_check_then_auth_nocfg + + .globl bad_transition_auth_then_auth_nocfg + .type bad_transition_auth_then_auth_nocfg,@function +bad_transition_auth_then_auth_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_auth_then_auth_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + autda x0, x2 + autda x0, x2 + pacda x0, x1 + autiasp + ret + .size bad_transition_auth_then_auth_nocfg, .-bad_transition_auth_then_auth_nocfg + + .globl bad_transition_check_then_check_nocfg + .type bad_transition_check_then_check_nocfg,@function +bad_transition_check_then_check_nocfg: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_transition_check_then_check_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + adr x3, 1f + br x3 +1: + ldr x2, [x0] + ldr x2, [x0] + pacda x0, x1 + autiasp + ret + .size bad_transition_check_then_check_nocfg, .-bad_transition_check_then_check_nocfg + +// Test resign with offset. + + .globl good_resign_with_increment_ldr + .type good_resign_with_increment_ldr,@function +good_resign_with_increment_ldr: +// CHECK-NOT: good_resign_with_increment_ldr + autda x0, x2 + add x0, x0, #8 + ldr x2, [x0] + sub x1, x0, #16 + mov x2, x1 + pacda x2, x3 + ret + .size good_resign_with_increment_ldr, .-good_resign_with_increment_ldr + + .globl good_resign_with_increment_brk + .type good_resign_with_increment_brk,@function +good_resign_with_increment_brk: +// CHECK-NOT: good_resign_with_increment_brk + autda x0, x2 + add x0, x0, #8 + eor x16, x0, x0, lsl #1 + tbz x16, #62, 1f + brk 0x1234 +1: + mov x2, x0 + pacda x2, x1 + ret + .size good_resign_with_increment_brk, .-good_resign_with_increment_brk + + .globl bad_nonconstant_auth_increment_check + .type bad_nonconstant_auth_increment_check,@function +bad_nonconstant_auth_increment_check: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_nonconstant_auth_increment_check, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + autda x0, x2 + add x0, x0, x1 + ldr x2, [x0] + pacda x0, x1 + ret + .size bad_nonconstant_auth_increment_check, .-bad_nonconstant_auth_increment_check + + .globl bad_nonconstant_auth_check_increment + .type bad_nonconstant_auth_check_increment,@function +bad_nonconstant_auth_check_increment: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_nonconstant_auth_check_increment, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: autda x0, x2 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: pacda x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ret + autda x0, x2 + ldr x2, [x0] + add x0, x0, x1 + pacda x0, x1 + ret + .size bad_nonconstant_auth_check_increment, .-bad_nonconstant_auth_check_increment + +// Test that all the expected signing instructions are recornized. + + .globl inst_pacda + .type inst_pacda,@function +inst_pacda: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacda, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacda x0, x1 + pacda x0, x1 + ret + .size inst_pacda, .-inst_pacda + + .globl inst_pacdza + .type inst_pacdza,@function +inst_pacdza: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdza, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdza x0 + pacdza x0 + ret + .size inst_pacdza, .-inst_pacdza + + .globl inst_pacdb + .type inst_pacdb,@function +inst_pacdb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdb x0, x1 + pacdb x0, x1 + ret + .size inst_pacdb, .-inst_pacdb + + .globl inst_pacdzb + .type inst_pacdzb,@function +inst_pacdzb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacdzb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacdzb x0 + pacdzb x0 + ret + .size inst_pacdzb, .-inst_pacdzb + + .globl inst_pacia + .type inst_pacia,@function +inst_pacia: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia x0, x1 + pacia x0, x1 + ret + .size inst_pacia, .-inst_pacia + + .globl inst_pacia1716 + .type inst_pacia1716,@function +inst_pacia1716: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia1716, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia1716 + pacia1716 + ret + .size inst_pacia1716, .-inst_pacia1716 + + .globl inst_paciasp + .type inst_paciasp,@function +inst_paciasp: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciasp, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciasp + mov x30, x0 + paciasp // signs LR + autiasp + ret + .size inst_paciasp, .-inst_paciasp + + .globl inst_paciaz + .type inst_paciaz,@function +inst_paciaz: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciaz, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciaz + mov x30, x0 + paciaz // signs LR + autiaz + ret + .size inst_paciaz, .-inst_paciaz + + .globl inst_paciza + .type inst_paciza,@function +inst_paciza: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciza, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 + paciza x0 + ret + .size inst_paciza, .-inst_paciza + + .globl inst_pacia171615 + .type inst_pacia171615,@function +inst_pacia171615: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacia171615, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacia171615 + mov x30, x0 + pacia171615 // signs LR + autia171615 + ret + .size inst_pacia171615, .-inst_pacia171615 + + .globl inst_paciasppc + .type inst_paciasppc,@function +inst_paciasppc: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_paciasppc, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciasppc + mov x30, x0 +1: + paciasppc // signs LR + autiasppc 1b + ret + .size inst_paciasppc, .-inst_paciasppc + + .globl inst_pacib + .type inst_pacib,@function +inst_pacib: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib x0, x1 + pacib x0, x1 + ret + .size inst_pacib, .-inst_pacib + + .globl inst_pacib1716 + .type inst_pacib1716,@function +inst_pacib1716: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib1716, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib1716 + pacib1716 + ret + .size inst_pacib1716, .-inst_pacib1716 + + .globl inst_pacibsp + .type inst_pacibsp,@function +inst_pacibsp: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibsp, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibsp + mov x30, x0 + pacibsp // signs LR + autibsp + ret + .size inst_pacibsp, .-inst_pacibsp + + .globl inst_pacibz + .type inst_pacibz,@function +inst_pacibz: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibz, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibz + mov x30, x0 + pacibz // signs LR + autibz + ret + .size inst_pacibz, .-inst_pacibz + + .globl inst_pacizb + .type inst_pacizb,@function +inst_pacizb: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacizb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacizb x0 + pacizb x0 + ret + .size inst_pacizb, .-inst_pacizb + + .globl inst_pacib171615 + .type inst_pacib171615,@function +inst_pacib171615: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacib171615, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacib171615 + mov x30, x0 + pacib171615 // signs LR + autib171615 + ret + .size inst_pacib171615, .-inst_pacib171615 + + .globl inst_pacibsppc + .type inst_pacibsppc,@function +inst_pacibsppc: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacibsppc, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacibsppc + mov x30, x0 +1: + pacibsppc // signs LR + autibsppc 1b + ret + .size inst_pacibsppc, .-inst_pacibsppc + + .globl inst_pacnbiasppc + .type inst_pacnbiasppc,@function +inst_pacnbiasppc: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacnbiasppc, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacnbiasppc + mov x30, x0 +1: + pacnbiasppc // signs LR + autiasppc 1b + ret + .size inst_pacnbiasppc, .-inst_pacnbiasppc + + .globl inst_pacnbibsppc + .type inst_pacnbibsppc,@function +inst_pacnbibsppc: +// CHECK-LABEL: GS-PAUTH: signing oracle found in function inst_pacnbibsppc, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: pacnbibsppc + mov x30, x0 +1: + pacnbibsppc // signs LR + autibsppc 1b + ret + .size inst_pacnbibsppc, .-inst_pacnbibsppc + + .globl main + .type main,@function +main: + mov x0, 0 + ret + .size main, .-main