[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect untrusted LR before tail call (PR #137224)
Anatoly Trosinenko via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue May 20 03:03:44 PDT 2025
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/137224
>From 0e859b759886700bcf551459dd9f5c4b22fbad9a Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 22 Apr 2025 21:43:14 +0300
Subject: [PATCH 1/2] [BOLT] Gadget scanner: detect untrusted LR before tail
call
Implement the detection of tail calls performed with untrusted link
register, which violates the assumption made on entry to every function.
Unlike other pauth gadgets, this one involves some amount of guessing
which branch instructions should be checked as tail calls.
---
bolt/lib/Passes/PAuthGadgetScanner.cpp | 94 ++-
.../AArch64/gs-pacret-autiasp.s | 31 +-
.../AArch64/gs-pauth-debug-output.s | 30 +-
.../AArch64/gs-pauth-tail-calls.s | 597 ++++++++++++++++++
4 files changed, 706 insertions(+), 46 deletions(-)
create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s
diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp
index 7a5d47a3ff812..dfb71575b2b39 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -701,8 +701,9 @@ class DataflowSrcSafetyAnalysis
//
// 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.
+// the same way basic blocks are processed by data-flow analysis, with the same
+// pessimistic estimation of the initial state at the start of each sequence
+// (except the first instruction of the function).
class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
BinaryFunction &BF;
MCPlusBuilder::AllocatorIdTy AllocId;
@@ -713,12 +714,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
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,
@@ -729,6 +724,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
}
void run() override {
+ const SrcState DefaultState = computePessimisticState(BF);
SrcState S = createEntryState();
for (auto &I : BF.instrs()) {
MCInst &Inst = I.second;
@@ -743,7 +739,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis {
LLVM_DEBUG({
traceInst(BC, "Due to label, resetting the state before", Inst);
});
- S = createUnsafeState();
+ S = DefaultState;
}
// Check if we need to remove an old annotation (this is the case if
@@ -1288,6 +1284,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,
return make_gadget_report(RetKind, Inst, *RetReg);
}
+/// While BOLT already marks some of the branch instructions as tail calls,
+/// this function tries to improve the coverage by including less obvious cases
+/// when it is possible to do without introducing too many false positives.
+static bool shouldAnalyzeTailCallInst(const BinaryContext &BC,
+ const BinaryFunction &BF,
+ const MCInstReference &Inst) {
+ // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()
+ // (such as isBranch at the time of writing this comment), some don't (such
+ // as isCall). For that reason, call MCInstrDesc's methods explicitly when
+ // it is important.
+ const MCInstrDesc &Desc =
+ BC.MII->get(static_cast<const MCInst &>(Inst).getOpcode());
+ // Tail call should be a branch (but not necessarily an indirect one).
+ if (!Desc.isBranch())
+ return false;
+
+ // Always analyze the branches already marked as tail calls by BOLT.
+ if (BC.MIB->isTailCall(Inst))
+ return true;
+
+ // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the
+ // below is a simplified condition from BinaryContext::printInstruction.
+ bool IsUnknownControlFlow =
+ BC.MIB->isIndirectBranch(Inst) && !BC.MIB->getJumpTable(Inst);
+
+ if (BF.hasCFG() && IsUnknownControlFlow)
+ return true;
+
+ return false;
+}
+
+static std::optional<PartialReport<MCPhysReg>>
+shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF,
+ const MCInstReference &Inst, const SrcState &S) {
+ static const GadgetKind UntrustedLRKind(
+ "untrusted link register found before tail call");
+
+ if (!shouldAnalyzeTailCallInst(BC, BF, Inst))
+ return std::nullopt;
+
+ // Not only the set of registers returned by getTrustedLiveInRegs() can be
+ // seen as a reasonable target-independent _approximation_ of "the LR", these
+ // are *exactly* those registers used by SrcSafetyAnalysis to initialize the
+ // set of trusted registers on function entry.
+ // Thus, this function basically checks that the precondition expected to be
+ // imposed by a function call instruction (which is hardcoded into the target-
+ // specific getTrustedLiveInRegs() function) is also respected on tail calls.
+ SmallVector<MCPhysReg> RegsToCheck = BC.MIB->getTrustedLiveInRegs();
+ LLVM_DEBUG({
+ traceInst(BC, "Found tail call inst", Inst);
+ traceRegMask(BC, "Trusted regs", S.TrustedRegs);
+ });
+
+ // In musl on AArch64, the _start function sets LR to zero and calls the next
+ // stage initialization function at the end, something along these lines:
+ //
+ // _start:
+ // mov x30, #0
+ // ; ... other initialization ...
+ // b _start_c ; performs "exit" system call at some point
+ //
+ // As this would produce a false positive for every executable linked with
+ // such libc, ignore tail calls performed by ELF entry function.
+ if (BC.StartFunctionAddress &&
+ *BC.StartFunctionAddress == Inst.getFunction()->getAddress()) {
+ LLVM_DEBUG({ dbgs() << " Skipping tail call in ELF entry function.\n"; });
+ return std::nullopt;
+ }
+
+ // Returns at most one report per instruction - this is probably OK...
+ for (auto Reg : RegsToCheck)
+ if (!S.TrustedRegs[Reg])
+ return make_gadget_report(UntrustedLRKind, Inst, Reg);
+
+ return std::nullopt;
+}
+
static std::optional<PartialReport<MCPhysReg>>
shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,
const SrcState &S) {
@@ -1444,6 +1517,9 @@ void FunctionAnalysisContext::findUnsafeUses(
if (PacRetGadgetsOnly)
return;
+ if (auto Report = shouldReportUnsafeTailCall(BC, BF, Inst, S))
+ Reports.push_back(*Report);
+
if (auto Report = shouldReportCallGadget(BC, Inst, S))
Reports.push_back(*Report);
if (auto Report = shouldReportSigningOracle(BC, Inst, S))
diff --git a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
index df0a83be00986..627f8eb20ab9c 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
@@ -224,20 +224,33 @@ 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
+// Without CFG, the state is reset at labels, assuming every register that can
+// be clobbered in the function was actually clobbered.
+
+ .globl lr_untouched_nocfg
+ .type lr_untouched_nocfg, at function
+lr_untouched_nocfg:
+// CHECK-NOT: lr_untouched_nocfg
+ adr x2, 1f
+ br x2
+1:
+ ret
+ .size lr_untouched_nocfg, .-lr_untouched_nocfg
+
+ .globl lr_clobbered_nocfg
+ .type lr_clobbered_nocfg, at function
+lr_clobbered_nocfg:
+// CHECK-LABEL: GS-PAUTH: non-protected ret found in function lr_clobbered_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:
+ b 2f
+ bl g // never executed, but affects the expected worst-case scenario
+2:
ret
- .size state_is_reset_after_indirect_branch_nocfg, .-state_is_reset_after_indirect_branch_nocfg
+ .size lr_clobbered_nocfg, .-lr_clobbered_nocfg
/// Now do a basic sanity check on every different Authentication instruction:
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 5aec945621987..75341063ab816 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s
@@ -199,8 +199,8 @@ nocfg:
// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: >)
// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: 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: , TrustedRegs: , Insts: >)
-// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , Insts: >)
+// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
+// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W30 W30_HI , TrustedRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: After src register safety analysis:
// CHECK-NEXT: Binary Function "nocfg" {
// CHECK-NEXT: Number : 3
@@ -224,32 +224,6 @@ nocfg:
// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: >
// CHECK-NEXT: RetReg: LR
// 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 , TrustedRegs: LR W30 W30_HI , Insts: [0]()>)
-// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: 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 , TrustedRegs: LR W0 W30 X0 W0_HI W30_HI , Insts: [0]()>)
-// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI , TrustedRegs: 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: , TrustedRegs: , Insts: [0]()>)
-// CHECK-NEXT: .. result: (src-state<SafeToDerefRegs: , TrustedRegs: , 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, TrustedRegs: BitVector, Insts: [0]()>
-// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-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 # CFGUnawareSrcSafetyAnalysis: src-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 # CFGUnawareSrcSafetyAnalysis: src-state<SafeToDerefRegs: BitVector, TrustedRegs: BitVector, Insts: [0]()>
.globl auth_oracle
.type auth_oracle, at function
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s b/bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s
new file mode 100644
index 0000000000000..2d3c2f1a632ca
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s
@@ -0,0 +1,597 @@
+// RUN: %clang %cflags -Wl,--entry=_custom_start -march=armv8.3-a %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
+
+// PACRET-NOT: untrusted link register found before tail call
+
+ .text
+
+ .globl callee
+ .type callee, at function
+callee:
+ ret
+ .size callee, .-callee
+
+ .globl good_direct_tailcall_no_clobber
+ .type good_direct_tailcall_no_clobber, at function
+good_direct_tailcall_no_clobber:
+// CHECK-NOT: good_direct_tailcall_no_clobber
+ b callee
+ .size good_direct_tailcall_no_clobber, .-good_direct_tailcall_no_clobber
+
+ .globl good_plt_tailcall_no_clobber
+ .type good_plt_tailcall_no_clobber, at function
+good_plt_tailcall_no_clobber:
+// CHECK-NOT: good_plt_tailcall_no_clobber
+ b callee_ext
+ .size good_plt_tailcall_no_clobber, .-good_plt_tailcall_no_clobber
+
+ .globl good_indirect_tailcall_no_clobber
+ .type good_indirect_tailcall_no_clobber, at function
+good_indirect_tailcall_no_clobber:
+// CHECK-NOT: good_indirect_tailcall_no_clobber
+ autia x0, x1
+ br x0
+ .size good_indirect_tailcall_no_clobber, .-good_indirect_tailcall_no_clobber
+
+ .globl bad_direct_tailcall_not_auted
+ .type bad_direct_tailcall_not_auted, at function
+bad_direct_tailcall_not_auted:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_not_auted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: b callee # TAILCALL
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ b callee
+ .size bad_direct_tailcall_not_auted, .-bad_direct_tailcall_not_auted
+
+ .globl bad_plt_tailcall_not_auted
+ .type bad_plt_tailcall_not_auted, at function
+bad_plt_tailcall_not_auted:
+// FIXME: Calls via PLT are disassembled incorrectly. Nevertheless, they are
+// still detected as tail calls.
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_plt_tailcall_not_auted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b bad_indirect_tailcall_not_auted # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: b bad_indirect_tailcall_not_auted # TAILCALL
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ b callee_ext
+ .size bad_plt_tailcall_not_auted, .-bad_plt_tailcall_not_auted
+
+ .globl bad_indirect_tailcall_not_auted
+ .type bad_indirect_tailcall_not_auted, at function
+bad_indirect_tailcall_not_auted:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_indirect_tailcall_not_auted, 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
+// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_not_auted, .-bad_indirect_tailcall_not_auted
+
+ .globl bad_direct_tailcall_untrusted
+ .type bad_direct_tailcall_untrusted, at function
+bad_direct_tailcall_untrusted:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_direct_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: b callee # TAILCALL
+// 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: b callee # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ b callee
+ .size bad_direct_tailcall_untrusted, .-bad_direct_tailcall_untrusted
+
+ .globl bad_plt_tailcall_untrusted
+ .type bad_plt_tailcall_untrusted, at function
+bad_plt_tailcall_untrusted:
+// FIXME: Calls via PLT are disassembled incorrectly. Nevertheless, they are
+// still detected as tail calls.
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_plt_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b bad_indirect_tailcall_untrusted # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_plt_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: b bad_indirect_tailcall_untrusted # TAILCALL
+// 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: b bad_indirect_tailcall_untrusted # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ b callee_ext
+ .size bad_plt_tailcall_untrusted, .-bad_plt_tailcall_untrusted
+
+ .globl bad_indirect_tailcall_untrusted
+ .type bad_indirect_tailcall_untrusted, at function
+bad_indirect_tailcall_untrusted:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_indirect_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_indirect_tailcall_untrusted, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: br x0 # TAILCALL
+// 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
+// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_untrusted, .-bad_indirect_tailcall_untrusted
+
+ .globl good_direct_tailcall_trusted
+ .type good_direct_tailcall_trusted, at function
+good_direct_tailcall_trusted:
+// CHECK-NOT: good_direct_tailcall_trusted
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b callee
+ .size good_direct_tailcall_trusted, .-good_direct_tailcall_trusted
+
+ .globl good_plt_tailcall_trusted
+ .type good_plt_tailcall_trusted, at function
+good_plt_tailcall_trusted:
+// CHECK-NOT: good_plt_tailcall_trusted
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b callee_ext
+ .size good_plt_tailcall_trusted, .-good_plt_tailcall_trusted
+
+ .globl good_indirect_tailcall_trusted
+ .type good_indirect_tailcall_trusted, at function
+good_indirect_tailcall_trusted:
+// CHECK-NOT: good_indirect_tailcall_trusted
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ autia x0, x1
+ br x0
+ .size good_indirect_tailcall_trusted, .-good_indirect_tailcall_trusted
+
+ .globl good_direct_tailcall_no_clobber_multi_bb
+ .type good_direct_tailcall_no_clobber_multi_bb, at function
+good_direct_tailcall_no_clobber_multi_bb:
+// CHECK-NOT: good_direct_tailcall_no_clobber_multi_bb
+ b 1f
+1:
+ b callee
+ .size good_direct_tailcall_no_clobber_multi_bb, .-good_direct_tailcall_no_clobber_multi_bb
+
+ .globl good_indirect_tailcall_no_clobber_multi_bb
+ .type good_indirect_tailcall_no_clobber_multi_bb, at function
+good_indirect_tailcall_no_clobber_multi_bb:
+// CHECK-NOT: good_indirect_tailcall_no_clobber_multi_bb
+ autia x0, x1
+ b 1f
+1:
+ br x0
+ .size good_indirect_tailcall_no_clobber_multi_bb_multi_bb, .-good_indirect_tailcall_no_clobber_multi_bb_multi_bb
+
+ .globl bad_direct_tailcall_not_auted_multi_bb
+ .type bad_direct_tailcall_not_auted_multi_bb, at function
+bad_direct_tailcall_not_auted_multi_bb:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_not_auted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ cbz x3, 1f
+ autiasp
+ ldr w2, [x30]
+1:
+ b callee
+ .size bad_direct_tailcall_not_auted_multi_bb, .-bad_direct_tailcall_not_auted_multi_bb
+
+ .globl bad_indirect_tailcall_not_auted_multi_bb
+ .type bad_indirect_tailcall_not_auted_multi_bb, at function
+bad_indirect_tailcall_not_auted_multi_bb:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_indirect_tailcall_not_auted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ cbz x3, 1f
+ autiasp
+ ldr w2, [x30]
+1:
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_not_auted_multi_bb, .-bad_indirect_tailcall_not_auted_multi_bb
+
+ .globl bad_direct_tailcall_untrusted_multi_bb
+ .type bad_direct_tailcall_untrusted_multi_bb, at function
+bad_direct_tailcall_untrusted_multi_bb:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_untrusted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_direct_tailcall_untrusted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: b callee # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ cbz x3, 1f
+ ldr w2, [x30]
+1:
+ b callee
+ .size bad_direct_tailcall_untrusted_multi_bb, .-bad_direct_tailcall_untrusted_multi_bb
+
+ .globl bad_indirect_tailcall_untrusted_multi_bb
+ .type bad_indirect_tailcall_untrusted_multi_bb, at function
+bad_indirect_tailcall_untrusted_multi_bb:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_indirect_tailcall_untrusted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_indirect_tailcall_untrusted_multi_bb, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 0 instructions that leak the affected registers are:
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ cbz x3, 1f
+ ldr w2, [x30]
+1:
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_untrusted_multi_bb, .-bad_indirect_tailcall_untrusted_multi_bb
+
+ .globl good_direct_tailcall_trusted_multi_bb
+ .type good_direct_tailcall_trusted_multi_bb, at function
+good_direct_tailcall_trusted_multi_bb:
+// CHECK-NOT: good_direct_tailcall_trusted_multi_bb
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b 1f
+1:
+ b callee
+ .size good_direct_tailcall_trusted_multi_bb, .-good_direct_tailcall_trusted_multi_bb
+
+ .globl good_indirect_tailcall_trusted_multi_bb
+ .type good_indirect_tailcall_trusted_multi_bb, at function
+good_indirect_tailcall_trusted_multi_bb:
+// CHECK-NOT: good_indirect_tailcall_trusted_multi_bb
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b 1f
+1:
+ autia x0, x1
+ br x0
+ .size good_indirect_tailcall_trusted_multi_bb, .-good_indirect_tailcall_trusted_multi_bb
+
+ .globl good_direct_tailcall_no_clobber_nocfg
+ .type good_direct_tailcall_no_clobber_nocfg, at function
+good_direct_tailcall_no_clobber_nocfg:
+// CHECK-NOT: good_direct_tailcall_no_clobber_nocfg
+ adr x3, 1f
+ br x3
+1:
+ b callee
+ .size good_direct_tailcall_no_clobber_nocfg, .-good_direct_tailcall_no_clobber_nocfg
+
+ .globl good_plt_tailcall_no_clobber_nocfg
+ .type good_plt_tailcall_no_clobber_nocfg, at function
+good_plt_tailcall_no_clobber_nocfg:
+// CHECK-NOT: good_plt_tailcall_no_clobber_nocfg
+ adr x3, 1f
+ br x3
+1:
+ b callee_ext
+ .size good_plt_tailcall_no_clobber_nocfg, .-good_plt_tailcall_no_clobber_nocfg
+
+ .globl good_indirect_tailcall_no_clobber_nocfg
+ .type good_indirect_tailcall_no_clobber_nocfg, at function
+good_indirect_tailcall_no_clobber_nocfg:
+// CHECK-NOT: good_indirect_tailcall_no_clobber_nocfg
+ adr x3, 1f
+ br x3
+1:
+ autia x0, x1
+ br x0
+ .size good_indirect_tailcall_no_clobber_nocfg, .-good_indirect_tailcall_no_clobber_nocfg
+
+ .globl bad_direct_tailcall_not_auted_nocfg
+ .type bad_direct_tailcall_not_auted_nocfg, at function
+bad_direct_tailcall_not_auted_nocfg:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_not_auted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ b callee
+ .size bad_direct_tailcall_not_auted_nocfg, .-bad_direct_tailcall_not_auted_nocfg
+
+ .globl bad_plt_tailcall_not_auted_nocfg
+ .type bad_plt_tailcall_not_auted_nocfg, at function
+bad_plt_tailcall_not_auted_nocfg:
+// FIXME: Calls via PLT are disassembled incorrectly. Nevertheless, they are
+// still detected as tail calls.
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_plt_tailcall_not_auted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b bad_indirect_tailcall_not_auted_nocfg # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ b callee_ext
+ .size bad_plt_tailcall_not_auted_nocfg, .-bad_plt_tailcall_not_auted_nocfg
+
+ .globl bad_indirect_tailcall_not_auted_nocfg
+ .type bad_indirect_tailcall_not_auted_nocfg, at function
+bad_indirect_tailcall_not_auted_nocfg:
+// Known false positive: ignoring UNKNOWN CONTROL FLOW without CFG.
+// CHECK-NOT: bad_indirect_tailcall_not_auted_nocfg
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_not_auted_nocfg, .-bad_indirect_tailcall_not_auted_nocfg
+
+ .globl bad_direct_tailcall_untrusted_nocfg
+ .type bad_direct_tailcall_untrusted_nocfg, at function
+bad_direct_tailcall_untrusted_nocfg:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_direct_tailcall_untrusted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_direct_tailcall_untrusted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: b callee # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ b callee
+ .size bad_direct_tailcall_untrusted_nocfg, .-bad_direct_tailcall_untrusted_nocfg
+
+ .globl bad_plt_tailcall_untrusted_nocfg
+ .type bad_plt_tailcall_untrusted_nocfg, at function
+bad_plt_tailcall_untrusted_nocfg:
+// FIXME: Calls via PLT are disassembled incorrectly. Nevertheless, they are
+// still detected as tail calls.
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_plt_tailcall_untrusted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b bad_indirect_tailcall_untrusted_nocfg # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_plt_tailcall_untrusted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: b bad_indirect_tailcall_untrusted_nocfg # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ b callee_ext
+ .size bad_plt_tailcall_untrusted_nocfg, .-bad_plt_tailcall_untrusted_nocfg
+
+ .globl bad_indirect_tailcall_untrusted_nocfg
+ .type bad_indirect_tailcall_untrusted_nocfg, at function
+bad_indirect_tailcall_untrusted_nocfg:
+// Known false negative: ignoring UNKNOWN CONTROL FLOW without CFG.
+// Authentication oracle is found by a generic checker, though.
+// CHECK-NOT: untrusted link register{{.*}}bad_indirect_tailcall_untrusted_nocfg
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_indirect_tailcall_untrusted_nocfg, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 0 instructions that leak the affected registers are:
+// CHECK-NOT: untrusted link register{{.*}}bad_indirect_tailcall_untrusted_nocfg
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ autia x0, x1
+ br x0
+ .size bad_indirect_tailcall_untrusted_nocfg, .-bad_indirect_tailcall_untrusted_nocfg
+
+ .globl good_direct_tailcall_trusted_nocfg
+ .type good_direct_tailcall_trusted_nocfg, at function
+good_direct_tailcall_trusted_nocfg:
+// CHECK-NOT: good_direct_tailcall_trusted_nocfg
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b callee
+ .size good_direct_tailcall_trusted_nocfg, .-good_direct_tailcall_trusted_nocfg
+
+ .globl good_plt_tailcall_trusted_nocfg
+ .type good_plt_tailcall_trusted_nocfg, at function
+good_plt_tailcall_trusted_nocfg:
+// CHECK-NOT: good_plt_tailcall_trusted_nocfg
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ b callee_ext
+ .size good_plt_tailcall_trusted_nocfg, .-good_plt_tailcall_trusted_nocfg
+
+ .globl good_indirect_tailcall_trusted_nocfg
+ .type good_indirect_tailcall_trusted_nocfg, at function
+good_indirect_tailcall_trusted_nocfg:
+// CHECK-NOT: good_indirect_tailcall_trusted_nocfg
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ adr x3, 1f
+ br x3
+1:
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ ldr w2, [x30]
+ autia x0, x1
+ br x0
+ .size good_indirect_tailcall_trusted_nocfg, .-good_indirect_tailcall_trusted_nocfg
+
+// Check Armv8.3-a fused auth+branch instructions.
+
+ .globl good_indirect_tailcall_no_clobber_v83
+ .type good_indirect_tailcall_no_clobber_v83, at function
+good_indirect_tailcall_no_clobber_v83:
+// CHECK-NOT: good_indirect_tailcall_no_clobber_v83
+ braa x0, x1
+ .size good_indirect_tailcall_no_clobber_v83, .-good_indirect_tailcall_no_clobber_v83
+
+ .globl bad_indirect_tailcall_untrusted_v83
+ .type bad_indirect_tailcall_untrusted_v83, at function
+bad_indirect_tailcall_untrusted_v83:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_indirect_tailcall_untrusted_v83, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: braa x0, x1 # TAILCALL
+// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_indirect_tailcall_untrusted_v83, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: The 1 instructions that leak the affected registers are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: braa x0, x1 # TAILCALL
+// 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: braa x0, x1 # TAILCALL
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ autiasp
+ braa x0, x1
+ .size bad_indirect_tailcall_untrusted_v83, .-bad_indirect_tailcall_untrusted_v83
+
+// Make sure ELF entry function does not generate false positive reports.
+// Additionally, check that the correct entry point is read from ELF header.
+
+ .globl _start
+ .type _start, at function
+_start:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function _start, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: b callee # TAILCALL
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x30, #0x0
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: mov x30, #0x0
+// CHECK-NEXT: {{[0-9a-f]+}}: b callee # TAILCALL
+ mov x30, #0
+ b callee
+ .size _start, .-_start
+
+ .globl _custom_start
+ .type _custom_start, at function
+_custom_start:
+// CHECK-NOT: _custom_start
+ mov x30, #0
+ b callee
+ .size _custom_start, .-_custom_start
+
+// Test two issues being reported for the same instruction.
+
+ .globl bad_non_protected_indirect_tailcall_not_auted
+ .type bad_non_protected_indirect_tailcall_not_auted, at function
+bad_non_protected_indirect_tailcall_not_auted:
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function bad_non_protected_indirect_tailcall_not_auted, 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]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x1]
+// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_non_protected_indirect_tailcall_not_auted, 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, [x1]
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x1]
+// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ ldr x0, [x1]
+ br x0
+ .size bad_non_protected_indirect_tailcall_not_auted, .-bad_non_protected_indirect_tailcall_not_auted
+
+ .globl main
+ .type main, at function
+main:
+ mov x0, 0
+ ret
+ .size main, .-main
>From f5aadbac069802e7f53993e49467f597434a4de8 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 14 May 2025 23:12:13 +0300
Subject: [PATCH 2/2] Account for improved nocfg analysis: update the tests
---
.../AArch64/gs-pauth-authentication-oracles.s | 19 -------------
.../AArch64/gs-pauth-signing-oracles.s | 27 -------------------
2 files changed, 46 deletions(-)
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s b/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
index d8dc8a5a31db0..b4a69e855a046 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
@@ -454,13 +454,10 @@ good_call_nocfg:
.type good_branch_nocfg, at function
good_branch_nocfg:
// CHECK-NOT: good_branch_nocfg
- paciasp
adr x2, 1f
br x2
1:
autia x0, x1
- autiasp // authenticate LR before tail call
- ldr x2, [x30] // check LR before tail call
br x0
.size good_branch_nocfg, .-good_branch_nocfg
@@ -468,14 +465,12 @@ good_branch_nocfg:
.type good_load_other_reg_nocfg, at function
good_load_other_reg_nocfg:
// CHECK-NOT: good_load_other_reg_nocfg
- paciasp
adr x2, 1f
br x2
1:
autia x0, x1
ldr x2, [x0]
- autiasp
ret
.size good_load_other_reg_nocfg, .-good_load_other_reg_nocfg
@@ -483,14 +478,12 @@ good_load_other_reg_nocfg:
.type good_load_same_reg_nocfg, at function
good_load_same_reg_nocfg:
// CHECK-NOT: good_load_same_reg_nocfg
- paciasp
adr x2, 1f
br x2
1:
autia x0, x1
ldr x0, [x0]
- autiasp
ret
.size good_load_same_reg_nocfg, .-good_load_same_reg_nocfg
@@ -502,13 +495,11 @@ bad_unchecked_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unchecked_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
- paciasp
adr x2, 1f
br x2
1:
autia x0, x1
- autiasp
ret
.size bad_unchecked_nocfg, .-bad_unchecked_nocfg
@@ -542,7 +533,6 @@ bad_unknown_usage_read_nocfg:
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul x3, x0, x1
- paciasp
adr x2, 1f
br x2
1:
@@ -550,7 +540,6 @@ bad_unknown_usage_read_nocfg:
mul x3, x0, x1
ldr x2, [x0]
- autiasp
ret
.size bad_unknown_usage_read_nocfg, .-bad_unknown_usage_read_nocfg
@@ -561,7 +550,6 @@ bad_unknown_usage_subreg_read_nocfg:
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul w3, w0, w1
- paciasp
adr x2, 1f
br x2
1:
@@ -569,7 +557,6 @@ bad_unknown_usage_subreg_read_nocfg:
mul w3, w0, w1
ldr x2, [x0]
- autiasp
ret
.size bad_unknown_usage_subreg_read_nocfg, .-bad_unknown_usage_subreg_read_nocfg
@@ -580,7 +567,6 @@ bad_unknown_usage_update_nocfg:
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: movk x0, #0x2a, lsl #16
- paciasp
adr x2, 1f
br x2
1:
@@ -588,7 +574,6 @@ bad_unknown_usage_update_nocfg:
movk x0, #42, lsl #16 // does not overwrite x0 completely
ldr x2, [x0]
- autiasp
ret
.size bad_unknown_usage_update_nocfg, .-bad_unknown_usage_update_nocfg
@@ -596,14 +581,12 @@ bad_unknown_usage_update_nocfg:
.type good_unknown_overwrite_nocfg, at function
good_unknown_overwrite_nocfg:
// CHECK-NOT: good_unknown_overwrite_nocfg
- paciasp
adr x2, 1f
br x2
1:
autia x0, x1
mul x0, x1, x2
- autiasp
ret
.size good_unknown_overwrite_nocfg, .-good_unknown_overwrite_nocfg
@@ -611,7 +594,6 @@ good_unknown_overwrite_nocfg:
.type good_address_arith_nocfg, at function
good_address_arith_nocfg:
// CHECK-NOT: good_address_arith_nocfg
- paciasp
adr x2, 1f
br x2
1:
@@ -625,7 +607,6 @@ good_address_arith_nocfg:
mov x1, #0
mov x2, #0
- autiasp
ret
.size good_address_arith_nocfg, .-good_address_arith_nocfg
diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
index 9b10879094a03..1ead398959fd6 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s
@@ -505,21 +505,16 @@ bad_one_auted_one_checked_multi_bb:
// * 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
@@ -527,14 +522,12 @@ good_sign_addr_mat_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
@@ -544,13 +537,11 @@ 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
@@ -560,13 +551,11 @@ 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
@@ -576,12 +565,10 @@ 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
@@ -592,13 +579,11 @@ bad_sign_plain_mem_nocfg:
// 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
@@ -609,14 +594,12 @@ bad_clobber_between_addr_mat_and_use_nocfg:
// 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
@@ -627,7 +610,6 @@ bad_clobber_between_auted_and_checked_nocfg:
// 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:
@@ -635,7 +617,6 @@ bad_clobber_between_auted_and_checked_nocfg:
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
@@ -646,7 +627,6 @@ bad_clobber_between_checked_and_used_nocfg:
// 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:
@@ -654,7 +634,6 @@ bad_clobber_between_checked_and_used_nocfg:
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
@@ -664,14 +643,12 @@ 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
@@ -681,14 +658,12 @@ 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
@@ -698,14 +673,12 @@ 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
More information about the llvm-branch-commits
mailing list