[llvm] [BOLT] Gadget scanner: analyze functions without CFG information (PR #133461)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 10 10:55:56 PDT 2025
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/133461
>From 168488e0949e450e2ee3d084d3321660e3330dd0 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 19 Mar 2025 18:58:32 +0300
Subject: [PATCH] [BOLT] Gadget scanner: analyze functions without CFG
information
Support simple analysis of the functions for which BOLT is unable to
reconstruct the CFG. This patch is inspired by the approach implemented
by Kristof Beyls in the original prototype of gadget scanner, but a
CFG-unaware counterpart of the data-flow analysis is implemented
instead of separate version of gadget detector, as multiple gadget kinds
are detected now.
---
bolt/include/bolt/Core/BinaryFunction.h | 13 +
bolt/include/bolt/Passes/PAuthGadgetScanner.h | 24 +
bolt/lib/Passes/PAuthGadgetScanner.cpp | 297 ++++++--
.../AArch64/gs-pacret-autiasp.s | 15 +
.../binary-analysis/AArch64/gs-pauth-calls.s | 652 ++++++++++++++++++
.../AArch64/gs-pauth-debug-output.s | 116 +++-
6 files changed, 1028 insertions(+), 89 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index d3d11f8c5fb73..5cb2cc95af695 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -799,6 +799,19 @@ class BinaryFunction {
return iterator_range<const_cfi_iterator>(cie_begin(), cie_end());
}
+ /// Iterate over instructions (only if CFG is unavailable or not built yet).
+ iterator_range<InstrMapType::iterator> instrs() {
+ assert(!hasCFG() && "Iterate over basic blocks instead");
+ return make_range(Instructions.begin(), Instructions.end());
+ }
+ iterator_range<InstrMapType::const_iterator> instrs() const {
+ assert(!hasCFG() && "Iterate over basic blocks instead");
+ return make_range(Instructions.begin(), Instructions.end());
+ }
+
+ /// Returns whether there are any labels at Offset.
+ bool hasLabelAt(unsigned Offset) const { return Labels.count(Offset) != 0; }
+
/// Iterate over all jump tables associated with this function.
iterator_range<std::map<uint64_t, JumpTable *>::const_iterator>
jumpTables() const {
diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h
index ee69337637da3..6765e2aff414f 100644
--- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h
+++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h
@@ -67,6 +67,14 @@ struct MCInstInBFReference {
uint64_t Offset;
MCInstInBFReference(BinaryFunction *BF, uint64_t Offset)
: BF(BF), Offset(Offset) {}
+
+ static MCInstInBFReference get(const MCInst *Inst, BinaryFunction &BF) {
+ for (auto &I : BF.instrs())
+ if (Inst == &I.second)
+ return MCInstInBFReference(&BF, I.first);
+ return {};
+ }
+
MCInstInBFReference() : BF(nullptr), Offset(0) {}
bool operator==(const MCInstInBFReference &RHS) const {
return BF == RHS.BF && Offset == RHS.Offset;
@@ -106,6 +114,12 @@ struct MCInstReference {
MCInstReference(BinaryFunction *BF, uint32_t Offset)
: MCInstReference(MCInstInBFReference(BF, Offset)) {}
+ static MCInstReference get(const MCInst *Inst, BinaryFunction &BF) {
+ if (BF.hasCFG())
+ return MCInstInBBReference::get(Inst, BF);
+ return MCInstInBFReference::get(Inst, BF);
+ }
+
bool operator<(const MCInstReference &RHS) const {
if (ParentKind != RHS.ParentKind)
return ParentKind < RHS.ParentKind;
@@ -140,6 +154,16 @@ struct MCInstReference {
llvm_unreachable("");
}
+ operator bool() const {
+ switch (ParentKind) {
+ case BasicBlockParent:
+ return U.BBRef.BB != nullptr;
+ case FunctionParent:
+ return U.BFRef.BF != nullptr;
+ }
+ llvm_unreachable("");
+ }
+
uint64_t getAddress() const {
switch (ParentKind) {
case BasicBlockParent:
diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp
index 2a20bcd09c786..08e0e914501de 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -265,21 +265,30 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const {
OS << ">";
}
-class SrcSafetyAnalysis
- : public DataflowAnalysis<SrcSafetyAnalysis, SrcState, /*Backward=*/false,
- SrcStatePrinter> {
- using Parent =
- DataflowAnalysis<SrcSafetyAnalysis, SrcState, false, SrcStatePrinter>;
- friend Parent;
-
+/// Computes which registers are safe to be used by control flow instructions.
+///
+/// This is the base class for two implementations: a dataflow-based analysis
+/// which is intended to be used for most functions and a simplified CFG-unaware
+/// version for functions without reconstructed CFG.
+class SrcSafetyAnalysis {
public:
- SrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,
+ SrcSafetyAnalysis(BinaryFunction &BF,
const std::vector<MCPhysReg> &RegsToTrackInstsFor)
- : Parent(BF, AllocId), NumRegs(BF.getBinaryContext().MRI->getNumRegs()),
+ : BC(BF.getBinaryContext()), NumRegs(BC.MRI->getNumRegs()),
RegsToTrackInstsFor(RegsToTrackInstsFor) {}
+
virtual ~SrcSafetyAnalysis() {}
+ static std::shared_ptr<SrcSafetyAnalysis>
+ create(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,
+ const std::vector<MCPhysReg> &RegsToTrackInstsFor);
+
+ virtual void run() = 0;
+ virtual ErrorOr<const SrcState &>
+ getStateBefore(const MCInst &Inst) const = 0;
+
protected:
+ BinaryContext &BC;
const unsigned NumRegs;
/// RegToTrackInstsFor is the set of registers for which the dataflow analysis
/// must compute which the last set of instructions writing to it are.
@@ -296,8 +305,6 @@ class SrcSafetyAnalysis
return S.LastInstWritingReg[Index];
}
- void preflight() {}
-
SrcState createEntryState() {
SrcState S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs())
@@ -305,36 +312,6 @@ class SrcSafetyAnalysis
return S;
}
- SrcState getStartingStateAtBB(const BinaryBasicBlock &BB) {
- if (BB.isEntryPoint())
- return createEntryState();
-
- return SrcState();
- }
-
- SrcState getStartingStateAtPoint(const MCInst &Point) { return SrcState(); }
-
- void doConfluence(SrcState &StateOut, const SrcState &StateIn) {
- SrcStatePrinter P(BC);
- LLVM_DEBUG({
- dbgs() << " SrcSafetyAnalysis::Confluence(\n";
- dbgs() << " State 1: ";
- P.print(dbgs(), StateOut);
- dbgs() << "\n";
- dbgs() << " State 2: ";
- P.print(dbgs(), StateIn);
- dbgs() << ")\n";
- });
-
- StateOut.merge(StateIn);
-
- LLVM_DEBUG({
- dbgs() << " merged state: ";
- P.print(dbgs(), StateOut);
- dbgs() << "\n";
- });
- }
-
BitVector getClobberedRegs(const MCInst &Point) const {
BitVector Clobbered(NumRegs, false);
// Assume a call can clobber all registers, including callee-saved
@@ -438,8 +415,6 @@ class SrcSafetyAnalysis
return Next;
}
- StringRef getAnnotationName() const { return StringRef("SrcSafetyAnalysis"); }
-
public:
std::vector<MCInstReference>
getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF,
@@ -458,14 +433,178 @@ class SrcSafetyAnalysis
}
std::vector<MCInstReference> Result;
for (const MCInst *Inst : LastWritingInsts) {
- MCInstInBBReference Ref = MCInstInBBReference::get(Inst, BF);
- assert(Ref.BB != nullptr && "Expected Inst to be found");
+ MCInstReference Ref = MCInstReference::get(Inst, BF);
+ assert(Ref && "Expected Inst to be found");
Result.push_back(MCInstReference(Ref));
}
return Result;
}
};
+class DataflowSrcSafetyAnalysis
+ : public SrcSafetyAnalysis,
+ public DataflowAnalysis<DataflowSrcSafetyAnalysis, SrcState,
+ /*Backward=*/false, SrcStatePrinter> {
+ using DFParent = DataflowAnalysis<DataflowSrcSafetyAnalysis, SrcState, false,
+ SrcStatePrinter>;
+ friend DFParent;
+
+ using SrcSafetyAnalysis::BC;
+ using SrcSafetyAnalysis::computeNext;
+
+public:
+ DataflowSrcSafetyAnalysis(BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocId,
+ const std::vector<MCPhysReg> &RegsToTrackInstsFor)
+ : SrcSafetyAnalysis(BF, RegsToTrackInstsFor), DFParent(BF, AllocId) {}
+
+ ErrorOr<const SrcState &> getStateBefore(const MCInst &Inst) const override {
+ return DFParent::getStateBefore(Inst);
+ }
+
+ void run() override { DFParent::run(); }
+
+protected:
+ void preflight() {}
+
+ SrcState getStartingStateAtBB(const BinaryBasicBlock &BB) {
+ if (BB.isEntryPoint())
+ return createEntryState();
+
+ return SrcState();
+ }
+
+ SrcState getStartingStateAtPoint(const MCInst &Point) { return SrcState(); }
+
+ void doConfluence(SrcState &StateOut, const SrcState &StateIn) {
+ SrcStatePrinter P(BC);
+ LLVM_DEBUG({
+ dbgs() << " DataflowSrcSafetyAnalysis::Confluence(\n";
+ dbgs() << " State 1: ";
+ P.print(dbgs(), StateOut);
+ dbgs() << "\n";
+ dbgs() << " State 2: ";
+ P.print(dbgs(), StateIn);
+ dbgs() << ")\n";
+ });
+
+ StateOut.merge(StateIn);
+
+ LLVM_DEBUG({
+ dbgs() << " merged state: ";
+ P.print(dbgs(), StateOut);
+ dbgs() << "\n";
+ });
+ }
+
+ StringRef getAnnotationName() const { return "DataflowSrcSafetyAnalysis"; }
+};
+
+// A simplified implementation of DataflowSrcSafetyAnalysis for functions
+// lacking CFG information.
+//
+// Let assume the instructions can only be executed linearly unless there is
+// a label to jump to - this should handle both directly jumping to a location
+// encoded as an immediate operand of a branch instruction, as well as saving a
+// branch destination somewhere and passing it to an indirect branch instruction
+// later, provided no arithmetic is performed on the destination address:
+//
+// ; good: the destination is directly encoded into the branch instruction
+// cbz x0, some_label
+//
+// ; good: the branch destination is first stored and then used as-is
+// adr x1, some_label
+// br x1
+//
+// ; bad: some clever arithmetic is performed manually
+// adr x1, some_label
+// add x1, x1, #4
+// br x1
+// ...
+// some_label:
+// ; pessimistically reset the state as we are unsure where we came from
+// ...
+// ret
+// JTI0:
+// .byte some_label - Ltmp0 ; computing offsets using labels may probably
+// work too, provided enough information is
+// retained by the assembler and linker
+//
+// Then, a function can be split into a number of disjoint contiguous sequences
+// of instructions without labels in between. These sequences can be processed
+// the same way basic blocks are processed by data-flow analysis, assuming
+// pessimistically that all registers are unsafe at the start of each sequence.
+class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
+ BinaryFunction &BF;
+ MCPlusBuilder::AllocatorIdTy AllocId;
+ unsigned StateAnnotationIndex;
+
+ void cleanStateAnnotations() {
+ for (auto &I : BF.instrs())
+ BC.MIB->removeAnnotation(I.second, StateAnnotationIndex);
+ }
+
+ /// Creates a state with all registers marked unsafe (not to be confused
+ /// with empty state).
+ SrcState createUnsafeState() const {
+ return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
+ }
+
+public:
+ CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocId,
+ const std::vector<MCPhysReg> &RegsToTrackInstsFor)
+ : SrcSafetyAnalysis(BF, RegsToTrackInstsFor), BF(BF), AllocId(AllocId) {
+ StateAnnotationIndex =
+ BC.MIB->getOrCreateAnnotationIndex("CFGUnawareSrcSafetyAnalysis");
+ }
+
+ void run() override {
+ SrcState S = createEntryState();
+ for (auto &I : BF.instrs()) {
+ MCInst &Inst = I.second;
+
+ // If there is a label before this instruction, it is possible that it
+ // can be jumped-to, thus conservatively resetting S. As an exception,
+ // let's ignore any labels at the beginning of the function, as at least
+ // one label is expected there.
+ if (BF.hasLabelAt(I.first) && &Inst != &BF.instrs().begin()->second) {
+ LLVM_DEBUG({
+ traceInst(BC, "Due to label, resetting the state before", Inst);
+ });
+ S = createUnsafeState();
+ }
+
+ // Check if we need to remove an old annotation (this is the case if
+ // this is the second, detailed, run of the analysis).
+ if (BC.MIB->hasAnnotation(Inst, StateAnnotationIndex))
+ BC.MIB->removeAnnotation(Inst, StateAnnotationIndex);
+ // Attach the state *before* this instruction executes.
+ BC.MIB->addAnnotation(Inst, StateAnnotationIndex, S, AllocId);
+
+ // Compute the state after this instruction executes.
+ S = computeNext(Inst, S);
+ }
+ }
+
+ ErrorOr<const SrcState &> getStateBefore(const MCInst &Inst) const override {
+ return BC.MIB->getAnnotationAs<SrcState>(Inst, StateAnnotationIndex);
+ }
+
+ ~CFGUnawareSrcSafetyAnalysis() { cleanStateAnnotations(); }
+};
+
+std::shared_ptr<SrcSafetyAnalysis>
+SrcSafetyAnalysis::create(BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocId,
+ const std::vector<MCPhysReg> &RegsToTrackInstsFor) {
+ if (BF.hasCFG())
+ return std::make_shared<DataflowSrcSafetyAnalysis>(BF, AllocId,
+ RegsToTrackInstsFor);
+ return std::make_shared<CFGUnawareSrcSafetyAnalysis>(BF, AllocId,
+ RegsToTrackInstsFor);
+}
+
static std::shared_ptr<Report>
shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
const SrcState &S) {
@@ -519,43 +658,51 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
return std::make_shared<GadgetReport>(CallKind, Inst, DestReg);
}
+template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {
+ if (BF.hasCFG()) {
+ for (BinaryBasicBlock &BB : BF)
+ for (int64_t I = 0, E = BB.size(); I < E; ++I)
+ Fn(MCInstInBBReference(&BB, I));
+ } else {
+ for (auto I : BF.instrs())
+ Fn(MCInstInBFReference(&BF, I.first));
+ }
+}
+
FunctionAnalysisResult
Analysis::findGadgets(BinaryFunction &BF,
MCPlusBuilder::AllocatorIdTy AllocatorId) {
FunctionAnalysisResult Result;
- SrcSafetyAnalysis PRA(BF, AllocatorId, {});
+ auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, {});
LLVM_DEBUG({ dbgs() << "Running src register safety analysis...\n"; });
- PRA.run();
+ Analysis->run();
LLVM_DEBUG({
dbgs() << "After src register safety analysis:\n";
BF.dump();
});
BinaryContext &BC = BF.getBinaryContext();
- for (BinaryBasicBlock &BB : BF) {
- for (int64_t I = 0, E = BB.size(); I < E; ++I) {
- MCInstReference Inst(&BB, I);
- const SrcState &S = *PRA.getStateBefore(Inst);
-
- // If non-empty state was never propagated from the entry basic block
- // to Inst, assume it to be unreachable and report a warning.
- if (S.empty()) {
- Result.Diagnostics.push_back(std::make_shared<GenericReport>(
- Inst, "Warning: unreachable instruction found"));
- continue;
- }
+ iterateOverInstrs(BF, [&](MCInstReference Inst) {
+ const SrcState &S = *Analysis->getStateBefore(Inst);
+
+ // If non-empty state was never propagated from the entry basic block
+ // to Inst, assume it to be unreachable and report a warning.
+ if (S.empty()) {
+ Result.Diagnostics.push_back(std::make_shared<GenericReport>(
+ Inst, "Warning: unreachable instruction found"));
+ return;
+ }
- if (auto Report = shouldReportReturnGadget(BC, Inst, S))
- Result.Diagnostics.push_back(Report);
+ if (auto Report = shouldReportReturnGadget(BC, Inst, S))
+ Result.Diagnostics.push_back(Report);
- if (PacRetGadgetsOnly)
- continue;
+ if (PacRetGadgetsOnly)
+ return;
- if (auto Report = shouldReportCallGadget(BC, Inst, S))
- Result.Diagnostics.push_back(Report);
- }
- }
+ if (auto Report = shouldReportCallGadget(BC, Inst, S))
+ Result.Diagnostics.push_back(Report);
+ });
return Result;
}
@@ -571,10 +718,10 @@ void Analysis::computeDetailedInfo(BinaryFunction &BF,
std::vector<MCPhysReg> RegsToTrackVec(RegsToTrack.begin(), RegsToTrack.end());
// Re-compute the analysis with register tracking.
- SrcSafetyAnalysis PRWIA(BF, AllocatorId, RegsToTrackVec);
+ auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, RegsToTrackVec);
LLVM_DEBUG(
{ dbgs() << "\nRunning detailed src register safety analysis...\n"; });
- PRWIA.run();
+ Analysis->run();
LLVM_DEBUG({
dbgs() << "After detailed src register safety analysis:\n";
BF.dump();
@@ -585,7 +732,7 @@ void Analysis::computeDetailedInfo(BinaryFunction &BF,
LLVM_DEBUG(
{ traceInst(BC, "Attaching clobbering info to", Report->Location); });
(void)BC;
- Report->setOverwritingInstrs(PRWIA.getLastClobberingInsts(
+ Report->setOverwritingInstrs(Analysis->getLastClobberingInsts(
Report->Location, BF, Report->getAffectedRegisters()));
}
}
@@ -598,9 +745,6 @@ void Analysis::runOnFunction(BinaryFunction &BF,
BF.dump();
});
- if (!BF.hasCFG())
- return;
-
FunctionAnalysisResult FAR = findGadgets(BF, AllocatorId);
if (FAR.Diagnostics.empty())
return;
@@ -686,8 +830,11 @@ void GadgetReport::generateReport(raw_ostream &OS,
};
if (OverwritingInstrs.size() == 1) {
const MCInstReference OverwInst = OverwritingInstrs[0];
- assert(OverwInst.ParentKind == MCInstReference::BasicBlockParent);
- reportFoundGadgetInSingleBBSingleOverwInst(OS, BC, OverwInst, Location);
+ // Printing the details for the MCInstReference::FunctionParent case
+ // is not implemented not to overcomplicate the code, as most functions
+ // are expected to have CFG information.
+ if (OverwInst.ParentKind == MCInstReference::BasicBlockParent)
+ reportFoundGadgetInSingleBBSingleOverwInst(OS, BC, OverwInst, Location);
}
}
diff --git a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
index d506ec13f4895..2193d40131478 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
@@ -223,6 +223,21 @@ f_unreachable_instruction:
ret
.size f_unreachable_instruction, .-f_unreachable_instruction
+// Expected false positive: without CFG, the state is reset to all-unsafe
+// after an unconditional branch.
+
+ .globl state_is_reset_after_indirect_branch_nocfg
+ .type state_is_reset_after_indirect_branch_nocfg, at function
+state_is_reset_after_indirect_branch_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected ret found in function state_is_reset_after_indirect_branch_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+ adr x2, 1f
+ br x2
+1:
+ ret
+ .size state_is_reset_after_indirect_branch_nocfg, .-state_is_reset_after_indirect_branch_nocfg
+
/// Now do a basic sanity check on every different Authentication instruction:
.globl f_autiasp
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s
index 0f6c850583dda..0b6bd5509a42a 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s
@@ -429,6 +429,324 @@ bad_indirect_call_mem_chain_of_auts_multi_bb:
ret
.size bad_indirect_call_mem_chain_of_auts_multi_bb, .-bad_indirect_call_mem_chain_of_auts_multi_bb
+// Tests for CFG-unaware analysis.
+//
+// All these tests use an instruction sequence like this
+//
+// adr x2, 1f
+// br x2
+// 1:
+// ; ...
+//
+// to make BOLT unable to reconstruct the control flow. Note that one can easily
+// tell whether the report corresponds to a function with or without CFG:
+// normally, the location of the gadget is described like this:
+//
+// ... found in function <function_name>, basic block <basic block name>, at address <address>
+//
+// When CFG information is not available, this is reduced to
+//
+// ... found in function <function_name>, at address <address>
+
+ .globl good_direct_call_nocfg
+ .type good_direct_call_nocfg, at function
+good_direct_call_nocfg:
+// CHECK-NOT: good_direct_call_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ bl callee
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_direct_call_nocfg, .-good_direct_call_nocfg
+
+ .globl good_indirect_call_arg_nocfg
+ .type good_indirect_call_arg_nocfg, at function
+good_indirect_call_arg_nocfg:
+// CHECK-NOT: good_indirect_call_arg_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ autia x0, x1
+ blr x0
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_indirect_call_arg_nocfg, .-good_indirect_call_arg_nocfg
+
+ .globl good_indirect_call_mem_nocfg
+ .type good_indirect_call_mem_nocfg, at function
+good_indirect_call_mem_nocfg:
+// CHECK-NOT: good_indirect_call_mem_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ autia x16, x0
+ blr x16
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_indirect_call_mem_nocfg, .-good_indirect_call_mem_nocfg
+
+ .globl good_indirect_call_arg_v83_nocfg
+ .type good_indirect_call_arg_v83_nocfg, at function
+good_indirect_call_arg_v83_nocfg:
+// CHECK-NOT: good_indirect_call_arg_v83_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ blraa x0, x1
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_indirect_call_arg_v83_nocfg, .-good_indirect_call_arg_v83_nocfg
+
+ .globl good_indirect_call_mem_v83_nocfg
+ .type good_indirect_call_mem_v83_nocfg, at function
+good_indirect_call_mem_v83_nocfg:
+// CHECK-NOT: good_indirect_call_mem_v83_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ blraa x16, x0
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_indirect_call_mem_v83_nocfg, .-good_indirect_call_mem_v83_nocfg
+
+ .globl bad_indirect_call_arg_nocfg
+ .type bad_indirect_call_arg_nocfg, at function
+bad_indirect_call_arg_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ blr x0
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size bad_indirect_call_arg_nocfg, .-bad_indirect_call_arg_nocfg
+
+ .globl obscure_indirect_call_arg_nocfg
+ .type obscure_indirect_call_arg_nocfg, at function
+obscure_indirect_call_arg_nocfg:
+// CHECK-NOCFG-LABEL: GS-PAUTH: non-protected call found in function obscure_indirect_call_arg_nocfg, at address
+// CHECK-NOCFG-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
+// CHECK-NOCFG-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ autia x0, x1 // not observed by the checker
+ b 1f
+1:
+ // The register state is pessimistically reset after a label, thus
+ // the below branch instruction is reported as non-protected - this is
+ // a known false-positive.
+ blr x0
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size obscure_good_indirect_call_arg_nocfg, .-obscure_good_indirect_call_arg_nocfg
+
+ .globl safe_lr_at_function_entry_nocfg
+ .type safe_lr_at_function_entry_nocfg, at function
+safe_lr_at_function_entry_nocfg:
+// CHECK-NOT: safe_lr_at_function_entry_nocfg
+ cbz x0, 1f
+ ret // LR is safe at the start of the function
+1:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ adr x2, 2f
+ br x2
+2:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size safe_lr_at_function_entry_nocfg, .-safe_lr_at_function_entry_nocfg
+
+ .globl lr_is_never_unsafe_before_first_inst_nocfg
+ .type lr_is_never_unsafe_before_first_inst_nocfg, at function
+// CHECK-NOT: lr_is_never_unsafe_before_first_inst_nocfg
+lr_is_never_unsafe_before_first_inst_nocfg:
+1:
+ // The register state is never reset before the first instruction of
+ // the function. This can lead to a known false-negative if LR is
+ // clobbered and then a jump to the very first instruction of the
+ // function is performed.
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ mov x30, x0
+ cbz x1, 1b
+
+ adr x2, 2f
+ br x2
+2:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size lr_is_never_unsafe_before_first_inst_nocfg, .-lr_is_never_unsafe_before_first_inst_nocfg
+
+ .globl bad_indirect_call_mem_nocfg
+ .type bad_indirect_call_mem_nocfg, at function
+bad_indirect_call_mem_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0]
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ blr x16
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size bad_indirect_call_mem_nocfg, .-bad_indirect_call_mem_nocfg
+
+ .globl bad_indirect_call_arg_clobber_nocfg
+ .type bad_indirect_call_arg_clobber_nocfg, at function
+bad_indirect_call_arg_clobber_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_clobber_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w2
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ autia x0, x1
+ mov w0, w2
+ blr x0
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size bad_indirect_call_arg_clobber_nocfg, .-bad_indirect_call_arg_clobber_nocfg
+
+ .globl bad_indirect_call_mem_clobber_nocfg
+ .type bad_indirect_call_mem_clobber_nocfg, at function
+bad_indirect_call_mem_clobber_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_clobber_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ autia x16, x0
+ mov w16, w2
+ blr x16
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size bad_indirect_call_mem_clobber_nocfg, .-bad_indirect_call_mem_clobber_nocfg
+
+ .globl good_indirect_call_mem_chain_of_auts_nocfg
+ .type good_indirect_call_mem_chain_of_auts_nocfg, at function
+good_indirect_call_mem_chain_of_auts_nocfg:
+// CHECK-NOT: good_indirect_call_mem_chain_of_auts_nocfg
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ autda x16, x1
+ ldr x16, [x16]
+ autia x16, x0
+ blr x16
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size good_indirect_call_mem_chain_of_auts_nocfg, .-good_indirect_call_mem_chain_of_auts_nocfg
+
+ .globl bad_indirect_call_mem_chain_of_auts_nocfg
+ .type bad_indirect_call_mem_chain_of_auts_nocfg, at function
+bad_indirect_call_mem_chain_of_auts_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_chain_of_auts_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x16]
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ autda x16, x1
+ ldr x16, [x16]
+ // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it.
+ blr x16
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size bad_indirect_call_mem_chain_of_auts_nocfg, .-bad_indirect_call_mem_chain_of_auts_nocfg
+
// Test tail calls. To somewhat decrease the number of test cases and not
// duplicate all of the above, only implement "mem" variant of test cases and
// mostly test negative cases.
@@ -537,6 +855,109 @@ bad_indirect_tailcall_mem_clobber_multi_bb:
br x16
.size bad_indirect_tailcall_mem_clobber_multi_bb, .-bad_indirect_tailcall_mem_clobber_multi_bb
+ .globl good_direct_tailcall_nocfg
+ .type good_direct_tailcall_nocfg, at function
+good_direct_tailcall_nocfg:
+// CHECK-NOT: good_direct_tailcall_nocfg
+ adr x2, 1f
+ br x2
+1:
+ b callee
+ .size good_direct_tailcall_nocfg, .-good_direct_tailcall_nocfg
+
+ .globl good_indirect_tailcall_mem_nocfg
+ .type good_indirect_tailcall_mem_nocfg, at function
+good_indirect_tailcall_mem_nocfg:
+// CHECK-NOT: good_indirect_tailcall_mem_nocfg
+ adr x2, 1f
+ br x2
+1:
+ ldr x16, [x0]
+ autia x16, x0
+ br x16
+ .size good_indirect_tailcall_mem_nocfg, .-good_indirect_tailcall_mem_nocfg
+
+ .globl good_indirect_tailcall_mem_v83_nocfg
+ .type good_indirect_tailcall_mem_v83_nocfg, at function
+good_indirect_tailcall_mem_v83_nocfg:
+// CHECK-NOT: good_indirect_tailcall_mem_v83_nocfg
+ adr x2, 1f
+ br x2
+1:
+ ldr x16, [x0]
+ braa x16, x0
+ .size good_indirect_tailcall_mem_v83_nocfg, .-good_indirect_tailcall_mem_v83_nocfg
+
+ .globl bad_indirect_tailcall_mem_nocfg
+ .type bad_indirect_tailcall_mem_nocfg, at function
+bad_indirect_tailcall_mem_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0]
+ adr x2, 1f
+ br x2
+1:
+ ldr x16, [x0]
+ br x16
+ .size bad_indirect_tailcall_mem_nocfg, .-bad_indirect_tailcall_mem_nocfg
+
+ .globl bad_indirect_tailcall_mem_clobber_nocfg
+ .type bad_indirect_tailcall_mem_clobber_nocfg, at function
+bad_indirect_tailcall_mem_clobber_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_clobber_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2
+ adr x2, 1f
+ br x2
+1:
+ ldr x16, [x0]
+ autia x16, x0
+ mov w16, w2
+ br x16
+ .size bad_indirect_tailcall_mem_clobber_nocfg, .-bad_indirect_tailcall_mem_clobber_nocfg
+
+ .globl bad_indirect_tailcall_mem_chain_of_auts_nocfg
+ .type bad_indirect_tailcall_mem_chain_of_auts_nocfg, at function
+bad_indirect_tailcall_mem_chain_of_auts_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_chain_of_auts_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x16]
+ adr x2, 1f
+ br x2
+1:
+ ldr x16, [x0]
+ autda x16, x1
+ ldr x16, [x16]
+ // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it.
+ br x16
+ .size bad_indirect_tailcall_mem_chain_of_auts_nocfg, .-bad_indirect_tailcall_mem_chain_of_auts_nocfg
+
+ .globl state_is_reset_at_branch_destination_nocfg
+ .type state_is_reset_at_branch_destination_nocfg, at function
+state_is_reset_at_branch_destination_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function state_is_reset_at_branch_destination_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ b 1f
+ autia x0, x1 // skipped
+1:
+ blr x0
+
+ adr x2, 2f
+ br x2
+2:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size state_is_reset_at_branch_destination_nocfg, .-state_is_reset_at_branch_destination_nocfg
+
// Test that calling a function is considered as invalidating safety of every
// register. Note that we only have to consider "returning" function calls
// (via branch-with-link), but both direct and indirect variants.
@@ -692,6 +1113,156 @@ indirect_call_invalidates_safety:
ret
.size indirect_call_invalidates_safety, .-indirect_call_invalidates_safety
+ .globl direct_call_invalidates_safety_nocfg
+ .type direct_call_invalidates_safety_nocfg, at function
+direct_call_invalidates_safety_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x2
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x8
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x10
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x18
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x20
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ mov x2, x0
+ autiza x2
+ bl callee
+ blr x2
+
+ mov x8, x0
+ autiza x8
+ bl callee
+ blr x8
+
+ mov x10, x0
+ autiza x10
+ bl callee
+ blr x10
+
+ mov x16, x0
+ autiza x16
+ bl callee
+ blr x16
+
+ mov x18, x0
+ autiza x18
+ bl callee
+ blr x18
+
+ mov x20, x0
+ autiza x20
+ bl callee
+ blr x20
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size direct_call_invalidates_safety_nocfg, .-direct_call_invalidates_safety_nocfg
+
+ .globl indirect_call_invalidates_safety_nocfg
+ .type indirect_call_invalidates_safety_nocfg, at function
+indirect_call_invalidates_safety_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x2
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x2
+// Check that only one error is reported per pair of BLRs.
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x2
+
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x8
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x8
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x8
+
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x10
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x10
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x10
+
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x16
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x16
+
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x18
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x18
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x18
+
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x20
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x20
+// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x20
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ mov x2, x0
+ autiza x2
+ blr x2 // protected call, but makes x2 unsafe
+ blr x2 // unprotected call
+
+ mov x8, x0
+ autiza x8
+ blr x8 // protected call, but makes x8 unsafe
+ blr x8 // unprotected call
+
+ mov x10, x0
+ autiza x10
+ blr x10 // protected call, but makes x10 unsafe
+ blr x10 // unprotected call
+
+ mov x16, x0
+ autiza x16
+ blr x16 // protected call, but makes x16 unsafe
+ blr x16 // unprotected call
+
+ mov x18, x0
+ autiza x18
+ blr x18 // protected call, but makes x18 unsafe
+ blr x18 // unprotected call
+
+ mov x20, x0
+ autiza x20
+ blr x20 // protected call, but makes x20 unsafe
+ blr x20 // unprotected call
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size indirect_call_invalidates_safety_nocfg, .-indirect_call_invalidates_safety_nocfg
+
// Test that fused auth+use Armv8.3 instruction do not mark register as safe.
.globl blraa_no_mark_safe
@@ -722,6 +1293,28 @@ blraa_no_mark_safe:
ret
.size blraa_no_mark_safe, .-blraa_no_mark_safe
+ .globl blraa_no_mark_safe_nocfg
+ .type blraa_no_mark_safe_nocfg, at function
+blraa_no_mark_safe_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function blraa_no_mark_safe_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: blraa x0, x1
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ blraa x0, x1 // safe, no write-back, clobbers everything
+ blr x0 // detected as unsafe
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size blraa_no_mark_safe_nocfg, .-blraa_no_mark_safe_nocfg
+
// Check that the correct set of registers is used to compute the set of last
// writing instructions: both x16 and x17 are tracked in this function, but
// only one particular register is used to compute the set of clobbering
@@ -774,6 +1367,65 @@ last_insts_writing_to_reg:
ret
.size last_insts_writing_to_reg, .-last_insts_writing_to_reg
+ .globl last_insts_writing_to_reg_nocfg
+ .type last_insts_writing_to_reg_nocfg, at function
+last_insts_writing_to_reg_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0]
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x17
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x17, [x1]
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ ldr x16, [x0]
+ blr x16
+ ldr x17, [x1]
+ blr x17
+
+ adr x2, 1f
+ br x2
+1:
+ ldp x29, x30, [sp], #16
+ autiasp
+ ret
+ .size last_insts_writing_to_reg_nocfg, .-last_insts_writing_to_reg_nocfg
+
+// Test that the instructions reported to the user are not cluttered with
+// annotations attached by data-flow analysis or its CFG-unaware counterpart.
+
+ .globl printed_instrs_dataflow
+ .type printed_instrs_dataflow, at function
+printed_instrs_dataflow:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function printed_instrs_dataflow, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL{{ *$}}
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}}
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}}
+// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL{{ *$}}
+ ldr x0, [x0]
+ br x0
+ .size printed_instrs_dataflow, .-printed_instrs_dataflow
+
+ .globl printed_instrs_nocfg
+ .type printed_instrs_nocfg, at function
+printed_instrs_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function printed_instrs_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW # Offset: 12{{ *$}}
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}}
+ adr x2, 1f
+ br x2
+1:
+ ldr x0, [x0]
+ br x0
+ .size printed_instrs_nocfg, .-printed_instrs_nocfg
+
.globl main
.type main, at function
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 cca1efe695168..e4a74bfe1e17a 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
@@ -57,7 +57,7 @@ simple:
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
-// CHECK-NEXT: SrcSafetyAnalysis::Confluence(
+// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence(
// CHECK-NEXT: State 1: src-state<empty>
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , Insts: >
@@ -71,7 +71,7 @@ simple:
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
-// CHECK-NEXT: SrcSafetyAnalysis::Confluence(
+// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence(
// CHECK-NEXT: State 1: src-state<SafeToDerefRegs: , Insts: >
// CHECK-NEXT: State 2: src-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: merged state: src-state<SafeToDerefRegs: , Insts: >
@@ -94,27 +94,27 @@ simple:
// CHECK-NEXT: }
// CHECK-NEXT: [[BB0]] (3 instructions, align : 1)
// CHECK-NEXT: Entry Point
-// CHECK-NEXT: 00000000: paciasp # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 00000008: b [[BB1]] # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: Successors: [[BB1]]
// CHECK-EMPTY:
// CHECK-NEXT: [[BB1]] (5 instructions, align : 1)
// CHECK-NEXT: Predecessors: [[BB0]]
-// CHECK-NEXT: 0000000c: autiza x0 # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 00000010: blr x0 # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 00000018: autiasp # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
-// CHECK-NEXT: 0000001c: ret # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: 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 # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
// PAUTH-NEXT: Call destination reg: X0
// PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}}
-// CHECK-NEXT: Found RET inst: 00000000: ret # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: RetReg: LR
// CHECK-NEXT: Authenticated reg: (none)
// CHECK-NEXT: SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}}
@@ -141,7 +141,7 @@ clobber:
// The above output was printed after first run of analysis
// CHECK-EMPTY:
-// CHECK-NEXT: Found RET inst: 00000000: ret # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: RetReg: LR
// CHECK-NEXT: Authenticated reg: (none)
// CHECK-NEXT: SafeToDerefRegs: W30_HI{{[ \t]*$}}
@@ -160,8 +160,96 @@ clobber:
// Iterating over the reports and attaching clobbering info:
// CHECK-EMPTY:
-// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # SrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
+// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0](0x{{[0-9a-f]+}} )>
+ .globl nocfg
+ .type nocfg, at function
+nocfg:
+ adr x0, 1f
+ br x0
+1:
+ ret
+ .size nocfg, .-nocfg
+
+// CHECK-LABEL:Analyzing in function nocfg, AllocatorId 1
+// CHECK-NEXT: Binary Function "nocfg" {
+// CHECK-NEXT: Number : 3
+// CHECK-NEXT: State : disassembled
+// ...
+// CHECK: IsSimple : 0
+// CHECK-NEXT: IsMultiEntry: 1
+// CHECK-NEXT: IsSplit : 0
+// CHECK-NEXT: BB Count : 0
+// CHECK-NEXT: Secondary Entry Points : __ENTRY_nocfg at 0x[[ENTRY_ADDR:[0-9a-f]+]]
+// CHECK-NEXT: }
+// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
+// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]]
+// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4
+// CHECK-NEXT: __ENTRY_nocfg at 0x[[ENTRY_ADDR]] (Entry Point):
+// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
+// CHECK-NEXT: 00000008: ret # Offset: 8
+// CHECK-NEXT: DWARF CFI Instructions:
+// CHECK-NEXT: <empty>
+// CHECK-NEXT: End of Function "nocfg"
+// CHECK-EMPTY:
+// CHECK-NEXT: Running src register safety analysis...
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
+// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , Insts: >)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: >)
+// CHECK-NEXT: After src register safety analysis:
+// CHECK-NEXT: Binary Function "nocfg" {
+// CHECK-NEXT: Number : 3
+// CHECK-NEXT: State : disassembled
+// ...
+// 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]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: __ENTRY_nocfg at 0x[[ENTRY_ADDR]] (Entry Point):
+// CHECK-NEXT: .{{[A-Za-z0-9]+}}:
+// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: 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 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: 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 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: >
+// CHECK-NEXT: RetReg: LR
+// CHECK-NEXT: Authenticated reg: (none)
+// CHECK-NEXT: SafeToDerefRegs:
+// CHECK-EMPTY:
+// CHECK-NEXT: Running detailed src register safety analysis...
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg at 0x[[ENTRY_ADDR]], src-state<SafeToDerefRegs: LR W30 W30_HI , Insts: [0]()>)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
+// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: , Insts: [0]()>)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , Insts: [0]()>)
+// CHECK-NEXT: After detailed src register safety analysis:
+// CHECK-NEXT: Binary Function "nocfg" {
+// CHECK-NEXT: Number : 3
+// ...
+// 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]] # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
+// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: 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 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: 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 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, Insts: [0]()>
// CHECK-LABEL:Analyzing in function main, AllocatorId 1
.globl main
More information about the llvm-commits
mailing list