[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
Wed May 28 12:07:50 PDT 2025


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

>From a75cab7070e2167a4be39a4467895a2d1622c4e8 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] [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        |  80 +++
 .../AArch64/gs-pauth-tail-calls.s             | 597 ++++++++++++++++++
 2 files changed, 677 insertions(+)
 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 6327a2da54d5b..4c7ae3c880db4 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -1307,6 +1307,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) {
@@ -1462,6 +1539,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-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



More information about the llvm-branch-commits mailing list