[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect signing oracles (PR #134146)

Anatoly Trosinenko via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Apr 7 03:38:47 PDT 2025


https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/134146

>From 83dc99e85494050a01abdbab7d5d868bb3546b79 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Mon, 24 Mar 2025 21:32:12 +0300
Subject: [PATCH] [BOLT] Gadget scanner: detect signing oracles

Implement the detection of signing oracles. In this patch, a signing
oracle is defined as a sign instruction that accepts a "non-protected"
pointer, but for a slightly different definition of "non-protected"
compared to control flow instructions.

A second BitVector named TrustedRegs is added to the register state
computed by the data-flow analysis. The difference between a
"safe-to-dereference" and a "trusted" register states is that to make
an unsafe register trusted by authentication, one has to make sure
that the authentication succeeded. For example, on AArch64 without
FEAT_PAuth2 and FEAT_EPAC, an authentication instruction produces an
invalid pointer on failure, so that subsequent memory access triggers
an error, but re-signing such pointer would "fix" the signature.

Note that while a separate "trusted" register state may be redundant
depending on the specific semantics of auth and sign operations, it is
still important to check signing operations: while code like this

    resign:
      autda x0, x1
      pacda x0, x2
      ret

is probably safe provided `autda` generates an error on authentication
failure, this function

    sign_anything:
      pacda x0, x1
      ret

is inherently unsafe.
---
 bolt/include/bolt/Core/MCPlusBuilder.h        |  40 +
 bolt/lib/Passes/PAuthGadgetScanner.cpp        | 151 ++-
 .../Target/AArch64/AArch64MCPlusBuilder.cpp   | 207 ++++
 .../AArch64/gs-pauth-address-checks.s         | 637 +++++++++++
 .../AArch64/gs-pauth-debug-output.s           | 139 +--
 .../AArch64/gs-pauth-signing-oracles.s        | 993 ++++++++++++++++++
 6 files changed, 2092 insertions(+), 75 deletions(-)
 create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
 create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index b5ad219cfc796..9d1b4bca7f6e8 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<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const {
     llvm_unreachable("not implemented");
     return getNoRegister();
@@ -622,6 +628,40 @@ class MCPlusBuilder {
     return std::make_pair(getNoRegister(), getNoRegister());
   }
 
+  /// Analyzes if a pointer is checked to be valid by the end of BB.
+  ///
+  /// 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 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 signing or authentication oracle.
+  ///
+  /// If this function returns a (Reg, Inst) pair, then it is known that in any
+  /// successor of BB either
+  /// * Reg is trusted, provided it was safe-to-dereference before Inst, or
+  /// * the program is terminated abnormally without introducing any signing
+  ///   or authentication oracles
+  virtual std::optional<std::pair<MCPhysReg, MCInst *>>
+  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 safe-to-dereference before execution of Inst,
+  /// it becomes trusted afterward (if MayOverwrite is false) or at least does
+  /// not escape in a way usable as an authentication oracle (if MayOverwrite
+  /// is true).
+  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 a768521fb13dc..2077f046b2c74 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -194,10 +194,24 @@ template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {
 ///   X30 is safe-to-dereference - the state computed for sub- and
 ///   super-registers is not inspected.
 struct State {
-  /// 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
@@ -210,7 +224,8 @@ struct State {
   State() {}
 
   State(unsigned NumRegs, unsigned NumRegsToTrack)
-      : SafeToDerefRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
+      : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),
+        LastInstWritingReg(NumRegsToTrack) {}
 
   State &merge(const State &StateIn) {
     if (StateIn.empty())
@@ -219,6 +234,7 @@ struct State {
       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);
@@ -231,6 +247,7 @@ struct State {
 
   bool operator==(const State &RHS) const {
     return SafeToDerefRegs == RHS.SafeToDerefRegs &&
+           TrustedRegs == RHS.TrustedRegs &&
            LastInstWritingReg == RHS.LastInstWritingReg;
   }
   bool operator!=(const State &RHS) const { return !((*this) == RHS); }
@@ -255,6 +272,7 @@ raw_ostream &operator<<(raw_ostream &OS, const State &S) {
     OS << "empty";
   } else {
     OS << "SafeToDerefRegs: " << S.SafeToDerefRegs << ", ";
+    OS << "TrustedRegs: " << S.TrustedRegs << ", ";
     printLastInsts(OS, S.LastInstWritingReg);
   }
   OS << ">";
@@ -275,11 +293,14 @@ void PacStatePrinter::print(raw_ostream &OS, const State &S) const {
   OS << "pacret-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);
   }
@@ -308,6 +329,17 @@ class PacRetAnalysis {
   /// 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<const MCInst *, std::pair<MCPhysReg, const MCInst *>>
+      CheckerSequenceInfo;
 
   SmallPtrSet<const MCInst *, 4> &lastWritingInsts(State &S,
                                                    MCPhysReg Reg) const {
@@ -322,8 +354,10 @@ class PacRetAnalysis {
 
   State createEntryState() {
     State S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
-    for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs())
-      S.SafeToDerefRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);
+    for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs()) {
+      S.TrustedRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);
+      S.SafeToDerefRegs = S.TrustedRegs;
+    }
     return S;
   }
 
@@ -370,6 +404,45 @@ class PacRetAnalysis {
     return Regs;
   }
 
+  // Returns all registers made trusted by this instruction.
+  SmallVector<MCPhysReg> getRegsMadeTrusted(const MCInst &Point,
+                                            const State &Cur) const {
+    SmallVector<MCPhysReg> 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, meaning
+      // it was deterministically processed a few steps before this instruction.
+      const State &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;
+  }
+
   State computeNext(const MCInst &Point, const State &Cur) {
     PacStatePrinter P(BC);
     LLVM_DEBUG({
@@ -396,11 +469,34 @@ class PacRetAnalysis {
     BitVector Clobbered = getClobberedRegs(Point);
     SmallVector<MCPhysReg> NewSafeToDerefRegs =
         getRegsMadeSafeToDeref(Point, Cur);
+    SmallVector<MCPhysReg> 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.
     State 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())
@@ -421,6 +517,10 @@ class PacRetAnalysis {
         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);
@@ -476,7 +576,22 @@ class PacRetDFAnalysis
     return DFParent::getStateBefore(Inst);
   }
 
-  void run() override { DFParent::run(); }
+  void run() override {
+    for (BinaryBasicBlock &BB : Func) {
+      if (auto CheckerInfo = BC.MIB->getAuthCheckedReg(BB)) {
+        MCInst *LastInstOfChecker = BB.getLastNonPseudoInstr();
+        LLVM_DEBUG({
+          dbgs() << "Found pointer checking sequence in " << BB.getName()
+                 << ":\n";
+          traceReg(BC, "Checked register", CheckerInfo->first);
+          traceInst(BC, "First instruction", *CheckerInfo->second);
+          traceInst(BC, "Last instruction", *LastInstOfChecker);
+        });
+        CheckerSequenceInfo[LastInstOfChecker] = *CheckerInfo;
+      }
+    }
+    DFParent::run();
+  }
 
 protected:
   void preflight() {}
@@ -634,6 +749,26 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
   return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
 }
 
+static std::shared_ptr<Report>
+shouldReportSigningOracle(const BinaryContext &BC, const MCInstReference &Inst,
+                          const State &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<GadgetReport>(SigningOracleKind, Inst, SignedReg);
+}
+
 FunctionAnalysisResult
 Analysis::findGadgets(BinaryFunction &BF,
                       MCPlusBuilder::AllocatorIdTy AllocatorId) {
@@ -666,6 +801,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 0d1908f91e514..5e6ae67df0269 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -256,6 +256,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<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const override {
     assert(isReturn(Inst));
     switch (Inst.getOpcode()) {
@@ -341,6 +371,183 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
     }
   }
 
+  std::optional<std::pair<MCPhysReg, MCInst *>>
+  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.
+    //
+    // 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) {
+      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..7dd84115129ba
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s
@@ -0,0 +1,637 @@
+// 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, at function
+raise_error:
+        ret
+        .size raise_error, .-raise_error
+
+        .globl  resign_no_check
+        .type   resign_no_check, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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
+
+        .globl  main
+        .type   main, at function
+main:
+        mov     x0, 0
+        ret
+        .size   main, .-main
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 8722581f861a5..6df73e3b22830 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
@@ -50,40 +50,40 @@ simple:
 // CHECK-NEXT:     <empty>
 // CHECK-NEXT: End of Function "simple"
 // CHECK-EMPTY:
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #25, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   stp     x29, x30, [sp, #-0x10]!, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   b       [[BB1]], pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #25, pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   stp     x29, x30, [sp, #-0x10]!, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   b       [[BB1]], pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
 // CHECK-NEXT:  PacRetAnalysis::Confluence(
 // CHECK-NEXT:    State 1: pacret-state<empty>
-// CHECK-NEXT:    State 2: pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:    merged state: pacret-state<SafeToDerefRegs: , Insts: >
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   autiza  x0, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   blr     x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ldp     x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #29, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT:    State 2: pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:    merged state: pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   autiza  x0, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   blr     x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ldp     x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #29, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
 // CHECK-NEXT:  PacRetAnalysis::Confluence(
-// CHECK-NEXT:    State 1: pacret-state<SafeToDerefRegs: , Insts: >
-// CHECK-NEXT:    State 2: pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:    merged state: pacret-state<SafeToDerefRegs: , Insts: >
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   autiza  x0, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   blr     x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ldp     x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #29, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT:    State 1: pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
+// CHECK-NEXT:    State 2: pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:    merged state: pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   autiza  x0, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   blr     x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ldp     x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   hint    #29, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: , Insts: >)
 // CHECK-NEXT:  After PacRetAnalysis:
 // CHECK-NEXT: Binary Function "simple"  {
 // CHECK-NEXT:   Number      : 1
@@ -93,27 +93,30 @@ simple:
 // CHECK-NEXT: }
 // CHECK-NEXT: [[BB0]] (3 instructions, align : 1)
 // CHECK-NEXT:   Entry Point
-// CHECK-NEXT:     00000000:   paciasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000004:   stp     x29, x30, [sp, #-0x10]! # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000008:   b       [[BB1]] # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000000:   paciasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000004:   stp     x29, x30, [sp, #-0x10]! # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000008:   b       [[BB1]] # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT:   Successors: [[BB1]]
 // CHECK-EMPTY:
 // CHECK-NEXT: [[BB1]] (5 instructions, align : 1)
 // CHECK-NEXT:   Predecessors: [[BB0]]
-// CHECK-NEXT:     0000000c:   autiza  x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000010:   blr     x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000014:   ldp     x29, x30, [sp], #0x10 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000018:   autiasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     0000001c:   ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:     0000000c:   autiza  x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000010:   blr     x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000014:   ldp     x29, x30, [sp], #0x10 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000018:   autiasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     0000001c:   ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-EMPTY:
 // CHECK-NEXT: DWARF CFI Instructions:
 // CHECK-NEXT:     <empty>
 // CHECK-NEXT: End of Function "simple"
 // CHECK-EMPTY:
-// PAUTH-NEXT:   Found call inst:     00000000:        blr     x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// PAUTH-NEXT:   Found sign inst:     00000000:        paciasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// PAUTH-NEXT:     Signed reg: LR
+// PAUTH-NEXT:     TrustedRegs: LR W30 W30_HI
+// PAUTH-NEXT:   Found call inst:     00000000:        blr     x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // PAUTH-NEXT:     Call destination reg: X0
 // PAUTH-NEXT:     SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}}
-// CHECK-NEXT:   Found RET inst:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:   Found RET inst:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT:     RetReg: LR
 // CHECK-NEXT:     Authenticated reg: (none)
 // CHECK-NEXT:     SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}}
@@ -127,10 +130,10 @@ clobber:
 
 // CHECK-LABEL:Analyzing in function clobber, AllocatorId 1
 // ...
-// CHECK:       PacRetAnalysis::ComputeNext(   mov     w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: >)
+// CHECK:       PacRetAnalysis::ComputeNext(   mov     w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: >)
 // CHECK-NEXT:  After PacRetAnalysis:
 // CHECK-NEXT: Binary Function "clobber"  {
 // ...
@@ -139,14 +142,14 @@ clobber:
 // The above output was printed after first run of analysis
 
 // CHECK-EMPTY:
-// CHECK-NEXT:   Found RET inst:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:   Found RET inst:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT:     RetReg: LR
 // CHECK-NEXT:     Authenticated reg: (none)
 // CHECK-NEXT:     SafeToDerefRegs: W30_HI{{[ \t]*$}}
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   mov     w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   mov     w30, #0x0, pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: [0]()>)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: W30_HI , TrustedRegs: W30_HI , Insts: [0](0x{{[0-9a-f]+}} )>)
 // CHECK-NEXT:  After detailed PacRetAnalysis:
 // CHECK-NEXT: Binary Function "clobber"  {
 // ...
@@ -156,7 +159,7 @@ clobber:
 // Iterating over the reports and attaching clobbering info:
 
 // CHECK-EMPTY:
-// CHECK-NEXT:   Attaching clobbering info to:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
+// CHECK-NEXT:   Attaching clobbering info to:     00000000:         ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
 
         .globl  nocfg
         .type   nocfg, at function
@@ -188,10 +191,10 @@ nocfg:
 // CHECK-NEXT:     <empty>
 // CHECK-NEXT: End of Function "nocfg"
 // CHECK-EMPTY:
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
 // CHECK-NEXT:  After PacRetAnalysis:
 // CHECK-NEXT: Binary Function "nocfg"  {
 // CHECK-NEXT:   Number      : 3
@@ -200,26 +203,26 @@ nocfg:
 // CHECK:        Secondary Entry Points : __ENTRY_nocfg at 0x[[ENTRY_ADDR]]
 // CHECK-NEXT: }
 // CHECK-NEXT: .{{[A-Za-z0-9]+}}:
-// CHECK-NEXT:     00000000:   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]] # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT:     00000004:   br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000000:   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]] # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000004:   br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT: __ENTRY_nocfg at 0x[[ENTRY_ADDR]] (Entry Point):
 // CHECK-NEXT: .{{[A-Za-z0-9]+}}:
-// CHECK-NEXT:     00000008:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:     00000008:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT: DWARF CFI Instructions:
 // CHECK-NEXT:     <empty>
 // CHECK-NEXT: End of Function "nocfg"
 // CHECK-EMPTY:
-// PAUTH-NEXT:   Found call inst:     00000000:        br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// PAUTH-NEXT:   Found call inst:     00000000:        br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // 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 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT:   Found RET inst:     00000000:         ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
 // CHECK-NEXT:     RetReg: LR
 // CHECK-NEXT:     Authenticated reg: (none)
 // CHECK-NEXT:     SafeToDerefRegs:
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
-// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: , Insts: [0]()>)
-// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , Insts: [0]()>)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], pacret-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: [0]()>)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
+// CHECK-NEXT:  PacRetAnalysis::ComputeNext(   ret     x30, pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: [0]()>)
+// CHECK-NEXT:   .. result: (pacret-state<SafeToDerefRegs: , TrustedRegs: , Insts: [0]()>)
 // CHECK-NEXT:  After detailed PacRetAnalysis:
 // CHECK-NEXT: Binary Function "nocfg"  {
 // CHECK-NEXT:   Number      : 3
@@ -227,16 +230,16 @@ nocfg:
 // CHECK:        Secondary Entry Points : __ENTRY_nocfg at 0x[[ENTRY_ADDR]]
 // CHECK-NEXT: }
 // CHECK-NEXT: .{{[A-Za-z0-9]+}}:
-// CHECK-NEXT:     00000000:   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]] # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0]()>
-// CHECK-NEXT:     00000004:   br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0]()>
+// CHECK-NEXT:     00000000:   adr     x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]] # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
+// CHECK-NEXT:     00000004:   br      x0 # UNKNOWN CONTROL FLOW # Offset: 4 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
 // CHECK-NEXT: __ENTRY_nocfg at 0x[[ENTRY_ADDR]] (Entry Point):
 // CHECK-NEXT: .{{[A-Za-z0-9]+}}:
-// CHECK-NEXT:     00000008:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0]()>
+// CHECK-NEXT:     00000008:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
 // CHECK-NEXT: DWARF CFI Instructions:
 // CHECK-NEXT:     <empty>
 // CHECK-NEXT: End of Function "nocfg"
 // CHECK-EMPTY:
-// CHECK-NEXT:   Attaching clobbering info to:     00000000:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: [0]()>
+// CHECK-NEXT:   Attaching clobbering info to:     00000000:   ret # Offset: 8 # NoCFGPacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
 
 // 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..2aaa3acddff0c
--- /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-pacret-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, at function
+sym:
+        ret
+        .size sym, .-sym
+
+        .globl  callee
+        .type   callee, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at function
+main:
+        mov     x0, 0
+        ret
+        .size   main, .-main



More information about the llvm-branch-commits mailing list