diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 2eadaf15d3a65..f928dd49edb25 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -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(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> +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 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> 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)) 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,@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