[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:15:29 PDT 2025
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/134146
>From 96b8b4d4b441ac802df95921bc736309de73166d 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