[BOLT] Gadget scanner: detect untrusted LR before tail call (#137224)

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, detection of this one involves some amount
of guessing which branch instructions should be checked as tail calls.
This commit is contained in:
Anatoly Trosinenko
2025-06-26 12:37:25 +03:00
committed by GitHub
parent 2e196a0ed1
commit 7a5af4f6b8
2 changed files with 684 additions and 0 deletions

View File

@@ -1319,6 +1319,90 @@ 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 detect less obvious cases, assuming false positives
/// are acceptable as long as there are not too many of them.
///
/// It is possible that not all the instructions classified as tail calls by
/// this function are safe to be considered as such for the purpose of code
/// transformations performed by BOLT. The intention of this function is to
/// spot some of actually missed tail calls (and likely a number of unrelated
/// indirect branch instructions) as long as this doesn't increase the amount
/// of false positive reports unacceptably.
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) {
@@ -1478,6 +1562,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))

View File

@@ -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,@function
callee:
ret
.size callee, .-callee
.globl good_direct_tailcall_no_clobber
.type good_direct_tailcall_no_clobber,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@function
main:
mov x0, 0
ret
.size main, .-main