diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 76ea2489e7038..8b6dc14121480 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -577,6 +577,16 @@ class MCPlusBuilder { return getNoRegister(); } + /// Returns the register used as call destination, or no-register, if not + /// an indirect call. Sets IsAuthenticatedInternally if the instruction + /// accepts a signed pointer as its operand and authenticates it internally. + virtual MCPhysReg + getRegUsedAsCallDest(const MCInst &Inst, + bool &IsAuthenticatedInternally) const { + llvm_unreachable("not implemented"); + return getNoRegister(); + } + virtual bool isTerminator(const MCInst &Inst) const; virtual bool isNoop(const MCInst &Inst) const { diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index 700059b814ab9..622e6721dea55 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -248,6 +248,9 @@ struct FunctionAnalysisResult { }; class Analysis : public BinaryFunctionPass { + /// Only search for pac-ret violations. + bool PacRetGadgetsOnly; + void runOnFunction(BinaryFunction &Function, MCPlusBuilder::AllocatorIdTy AllocatorId); FunctionAnalysisResult findGadgets(BinaryFunction &BF, @@ -261,7 +264,8 @@ class Analysis : public BinaryFunctionPass { std::mutex AnalysisResultsMutex; public: - explicit Analysis() : BinaryFunctionPass(false) {} + explicit Analysis(bool PacRetGadgetsOnly) + : BinaryFunctionPass(false), PacRetGadgetsOnly(PacRetGadgetsOnly) {} const char *getName() const override { return "pauth-gadget-scanner"; } diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h index 19f8c6b2646d7..3de945f6a1507 100644 --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -81,9 +81,9 @@ extern llvm::cl::opt Verbosity; /// Return true if we should process all functions in the binary. bool processAllFunctions(); -enum GadgetScannerKind { GS_PACRET, GS_ALL }; +enum GadgetScannerKind { GS_PACRET, GS_PAUTH, GS_ALL }; -extern llvm::cl::list GadgetScannersToRun; +extern llvm::cl::bits GadgetScannersToRun; } // namespace opts diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index e9940372f5c92..a3b320c545734 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -401,11 +401,11 @@ class PacRetAnalysis public: std::vector - getLastClobberingInsts(const MCInst Ret, BinaryFunction &BF, - const ArrayRef UsedDirtyRegs) const { + getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF, + const ArrayRef UsedDirtyRegs) { if (RegsToTrackInstsFor.empty()) return {}; - auto MaybeState = getStateAt(Ret); + auto MaybeState = getStateBefore(Inst); if (!MaybeState) llvm_unreachable("Expected State to be present"); const State &S = *MaybeState; @@ -453,6 +453,29 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst, return std::make_shared(RetKind, Inst, RetReg); } +static std::shared_ptr +shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst, + const State &S) { + static const GadgetKind CallKind("non-protected call found"); + if (!BC.MIB->isCall(Inst) && !BC.MIB->isBranch(Inst)) + return nullptr; + + bool IsAuthenticated = false; + MCPhysReg DestReg = BC.MIB->getRegUsedAsCallDest(Inst, IsAuthenticated); + if (IsAuthenticated || DestReg == BC.MIB->getNoRegister()) + return nullptr; + + LLVM_DEBUG({ + traceInst(BC, "Found call inst", Inst); + traceReg(BC, "Call destination reg", DestReg); + traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs); + }); + if (S.SafeToDerefRegs[DestReg]) + return nullptr; + + return std::make_shared(CallKind, Inst, DestReg); +} + FunctionAnalysisResult Analysis::findGadgets(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) { @@ -469,7 +492,7 @@ Analysis::findGadgets(BinaryFunction &BF, for (BinaryBasicBlock &BB : BF) { for (int64_t I = 0, E = BB.size(); I < E; ++I) { MCInstReference Inst(&BB, I); - const State &S = *PRA.getStateAt(Inst); + const State &S = *PRA.getStateBefore(Inst); // If non-empty state was never propagated from the entry basic block // to Inst, assume it to be unreachable and report a warning. @@ -481,6 +504,12 @@ Analysis::findGadgets(BinaryFunction &BF, if (auto Report = shouldReportReturnGadget(BC, Inst, S)) Result.Diagnostics.push_back(Report); + + if (PacRetGadgetsOnly) + continue; + + if (auto Report = shouldReportCallGadget(BC, Inst, S)) + Result.Diagnostics.push_back(Report); } } return Result; diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 409c2bbe35d4e..627ccd30add39 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -247,12 +247,14 @@ static cl::opt WriteBoltInfoSection( "bolt-info", cl::desc("write bolt info section in the output binary"), cl::init(true), cl::Hidden, cl::cat(BoltOutputCategory)); -cl::list - GadgetScannersToRun("scanners", cl::desc("which gadget scanners to run"), - cl::values(clEnumValN(GS_PACRET, "pacret", "pac-ret"), - clEnumValN(GS_ALL, "all", "all")), - cl::ZeroOrMore, cl::CommaSeparated, - cl::cat(BinaryAnalysisCategory)); +cl::bits GadgetScannersToRun( + "scanners", cl::desc("which gadget scanners to run"), + cl::values( + clEnumValN(GS_PACRET, "pacret", + "pac-ret: return address protection (subset of \"pauth\")"), + clEnumValN(GS_PAUTH, "pauth", "All Pointer Authentication scanners"), + clEnumValN(GS_ALL, "all", "All implemented scanners")), + cl::ZeroOrMore, cl::CommaSeparated, cl::cat(BinaryAnalysisCategory)); } // namespace opts @@ -3539,12 +3541,18 @@ void RewriteInstance::runBinaryAnalyses() { // FIXME: add a pass that warns about which functions do not have CFG, // and therefore, analysis is most likely to be less accurate. using GSK = opts::GadgetScannerKind; - // if no command line option was given, act as if "all" was specified. - if (opts::GadgetScannersToRun.empty()) - opts::GadgetScannersToRun.addValue(GSK::GS_ALL); - for (GSK ScannerToRun : opts::GadgetScannersToRun) { - if (ScannerToRun == GSK::GS_PACRET || ScannerToRun == GSK::GS_ALL) - Manager.registerPass(std::make_unique()); + using PAuthScanner = PAuthGadgetScanner::Analysis; + + // If no command line option was given, act as if "all" was specified. + bool RunAll = !opts::GadgetScannersToRun.getBits() || + opts::GadgetScannersToRun.isSet(GSK::GS_ALL); + + if (RunAll || opts::GadgetScannersToRun.isSet(GSK::GS_PAUTH)) { + Manager.registerPass( + std::make_unique(/*OnlyPacRetChecks=*/false)); + } else if (RunAll || opts::GadgetScannersToRun.isSet(GSK::GS_PACRET)) { + Manager.registerPass( + std::make_unique(/*OnlyPacRetChecks=*/true)); } BC->logBOLTErrorsAndQuitOnFatal(Manager.runPasses()); diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index d238a1df5c7d7..9b01b39251c29 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -277,6 +277,33 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { } } + MCPhysReg + getRegUsedAsCallDest(const MCInst &Inst, + bool &IsAuthenticatedInternally) const override { + assert(isCall(Inst) || isBranch(Inst)); + IsAuthenticatedInternally = false; + + switch (Inst.getOpcode()) { + case AArch64::BR: + case AArch64::BLR: + return Inst.getOperand(0).getReg(); + case AArch64::BRAA: + case AArch64::BRAB: + case AArch64::BRAAZ: + case AArch64::BRABZ: + case AArch64::BLRAA: + case AArch64::BLRAB: + case AArch64::BLRAAZ: + case AArch64::BLRABZ: + IsAuthenticatedInternally = true; + return Inst.getOperand(0).getReg(); + default: + if (isIndirectCall(Inst) || isIndirectBranch(Inst)) + llvm_unreachable("Unhandled indirect branch"); + return getNoRegister(); + } + } + bool isADRP(const MCInst &Inst) const override { return Inst.getOpcode() == AArch64::ADRP; } diff --git a/bolt/test/binary-analysis/AArch64/cmdline-args.test b/bolt/test/binary-analysis/AArch64/cmdline-args.test index 1204d5b1289af..76f7c3ba0a1c7 100644 --- a/bolt/test/binary-analysis/AArch64/cmdline-args.test +++ b/bolt/test/binary-analysis/AArch64/cmdline-args.test @@ -33,7 +33,8 @@ HELP-EMPTY: HELP-NEXT: BinaryAnalysis options: HELP-EMPTY: HELP-NEXT: --scanners= - which gadget scanners to run -HELP-NEXT: =pacret - pac-ret -HELP-NEXT: =all - all +HELP-NEXT: =pacret - pac-ret: return address protection (subset of "pauth") +HELP-NEXT: =pauth - All Pointer Authentication scanners +HELP-NEXT: =all - All implemented scanners HELP-EMPTY: HELP-NEXT: Generic Options: diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s new file mode 100644 index 0000000000000..0f6c850583dda --- /dev/null +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s @@ -0,0 +1,782 @@ +// RUN: %clang %cflags -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: non-protected call found in function + + .text + + .globl callee + .type callee,@function +callee: + ret + .size callee, .-callee + + .globl good_direct_call + .type good_direct_call,@function +good_direct_call: +// CHECK-NOT: good_direct_call + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + bl callee + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_direct_call, .-good_direct_call + + .globl good_indirect_call_arg + .type good_indirect_call_arg,@function +good_indirect_call_arg: +// CHECK-NOT: good_indirect_call_arg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + blr x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_arg, .-good_indirect_call_arg + + .globl good_indirect_call_mem + .type good_indirect_call_mem,@function +good_indirect_call_mem: +// CHECK-NOT: good_indirect_call_mem + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autia x16, x0 + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem, .-good_indirect_call_mem + + .globl good_indirect_call_arg_v83 + .type good_indirect_call_arg_v83,@function +good_indirect_call_arg_v83: +// CHECK-NOT: good_indirect_call_arg_v83 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + blraa x0, x1 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_arg_v83, .-good_indirect_call_arg_v83 + + .globl good_indirect_call_mem_v83 + .type good_indirect_call_mem_v83,@function +good_indirect_call_mem_v83: +// CHECK-NOT: good_indirect_call_mem_v83 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + blraa x16, x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_v83, .-good_indirect_call_mem_v83 + + .globl bad_indirect_call_arg + .type bad_indirect_call_arg,@function +bad_indirect_call_arg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + blr x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg, .-bad_indirect_call_arg + + .globl bad_indirect_call_mem + .type bad_indirect_call_mem,@function +bad_indirect_call_mem: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem, .-bad_indirect_call_mem + + .globl bad_indirect_call_arg_clobber + .type bad_indirect_call_arg_clobber,@function +bad_indirect_call_arg_clobber: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_clobber, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w2 +// 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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w2 +// CHECK-NEXT: {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + mov w0, w2 + blr x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg_clobber, .-bad_indirect_call_arg_clobber + + .globl bad_indirect_call_mem_clobber + .type bad_indirect_call_mem_clobber,@function +bad_indirect_call_mem_clobber: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_clobber, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2 +// 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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: autia x16, x0 +// CHECK-NEXT: {{[0-9a-f]+}}: mov w16, w2 +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autia x16, x0 + mov w16, w2 + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_clobber, .-bad_indirect_call_mem_clobber + + .globl good_indirect_call_mem_chain_of_auts + .type good_indirect_call_mem_chain_of_auts,@function +good_indirect_call_mem_chain_of_auts: +// CHECK-NOT: good_indirect_call_mem_chain_of_auts + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + autia x16, x0 + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_chain_of_auts, .-good_indirect_call_mem_chain_of_auts + + .globl bad_indirect_call_mem_chain_of_auts + .type bad_indirect_call_mem_chain_of_auts,@function +bad_indirect_call_mem_chain_of_auts: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_chain_of_auts, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x16] +// 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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: autda x16, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x16] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it. + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_chain_of_auts, .-bad_indirect_call_mem_chain_of_auts + +// Multi-BB test cases. +// +// Positive ("good") test cases are designed so that the register is made safe +// in one BB and used in the other. Negative ("bad") ones are designed so that +// there are two predecessors, one of them ends with the register in a safe +// state and the other ends with that register being unsafe. + + .globl good_indirect_call_arg_multi_bb + .type good_indirect_call_arg_multi_bb,@function +good_indirect_call_arg_multi_bb: +// CHECK-NOT: good_indirect_call_arg_multi_bb + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + cbz x2, 1f + blr x0 +1: + ldr x1, [x0] // prevent authentication oracle + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_arg_multi_bb, .-good_indirect_call_arg_multi_bb + + .globl good_indirect_call_mem_multi_bb + .type good_indirect_call_mem_multi_bb,@function +good_indirect_call_mem_multi_bb: +// CHECK-NOT: good_indirect_call_mem_multi_bb + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autia x16, x0 + cbz x2, 1f + blr x16 +1: + ldr w0, [x16] // prevent authentication oracle + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_multi_bb, .-good_indirect_call_mem_multi_bb + + .globl bad_indirect_call_arg_multi_bb + .type bad_indirect_call_arg_multi_bb,@function +bad_indirect_call_arg_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + cbz x2, 1f + autia x0, x1 +1: + blr x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg_multi_bb, .-bad_indirect_call_arg_multi_bb + + .globl bad_indirect_call_mem_multi_bb + .type bad_indirect_call_mem_multi_bb,@function +bad_indirect_call_mem_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0] + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + cbz x2, 1f + autia x16, x1 +1: + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_multi_bb, .-bad_indirect_call_mem_multi_bb + + .globl bad_indirect_call_arg_clobber_multi_bb + .type bad_indirect_call_arg_clobber_multi_bb,@function +bad_indirect_call_arg_clobber_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_clobber_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w0, w3 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + cbz x2, 1f + mov w0, w3 +1: + blr x0 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg_clobber_multi_bb, .-bad_indirect_call_arg_clobber_multi_bb + + .globl bad_indirect_call_mem_clobber_multi_bb + .type bad_indirect_call_mem_clobber_multi_bb,@function +bad_indirect_call_mem_clobber_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_clobber_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autia x16, x0 + cbz x2, 1f + mov w16, w2 +1: + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_clobber_multi_bb, .-bad_indirect_call_mem_clobber_multi_bb + + .globl good_indirect_call_mem_chain_of_auts_multi_bb + .type good_indirect_call_mem_chain_of_auts_multi_bb,@function +good_indirect_call_mem_chain_of_auts_multi_bb: +// CHECK-NOT: good_indirect_call_mem_chain_of_auts_multi_bb + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + autia x16, x0 + cbz x2, 1f + blr x16 +1: + ldr w0, [x16] // prevent authentication oracle + + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_chain_of_auts_multi_bb, .-good_indirect_call_mem_chain_of_auts_multi_bb + + .globl bad_indirect_call_mem_chain_of_auts_multi_bb + .type bad_indirect_call_mem_chain_of_auts_multi_bb,@function +bad_indirect_call_mem_chain_of_auts_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_chain_of_auts_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x16] + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + cbz x2, 1f + autia x16, x0 +1: + blr x16 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_chain_of_auts_multi_bb, .-bad_indirect_call_mem_chain_of_auts_multi_bb + +// Test tail calls. To somewhat decrease the number of test cases and not +// duplicate all of the above, only implement "mem" variant of test cases and +// mostly test negative cases. + + .globl good_direct_tailcall + .type good_direct_tailcall,@function +good_direct_tailcall: +// CHECK-NOT: good_direct_tailcall + b callee + .size good_direct_tailcall, .-good_direct_tailcall + + .globl good_indirect_tailcall_mem + .type good_indirect_tailcall_mem,@function +good_indirect_tailcall_mem: +// CHECK-NOT: good_indirect_tailcall_mem + ldr x16, [x0] + autia x16, x0 + br x16 + .size good_indirect_tailcall_mem, .-good_indirect_tailcall_mem + + .globl good_indirect_tailcall_mem_v83 + .type good_indirect_tailcall_mem_v83,@function +good_indirect_tailcall_mem_v83: +// CHECK-NOT: good_indirect_tailcall_mem_v83 + ldr x16, [x0] + braa x16, x0 + .size good_indirect_tailcall_mem_v83, .-good_indirect_tailcall_mem_v83 + + .globl bad_indirect_tailcall_mem + .type bad_indirect_tailcall_mem,@function +bad_indirect_tailcall_mem: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: br x16 + ldr x16, [x0] + br x16 + .size bad_indirect_tailcall_mem, .-bad_indirect_tailcall_mem + + .globl bad_indirect_tailcall_mem_clobber + .type bad_indirect_tailcall_mem_clobber,@function +bad_indirect_tailcall_mem_clobber: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_clobber, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2 +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: autia x16, x0 +// CHECK-NEXT: {{[0-9a-f]+}}: mov w16, w2 +// CHECK-NEXT: {{[0-9a-f]+}}: br x16 + ldr x16, [x0] + autia x16, x0 + mov w16, w2 + br x16 + .size bad_indirect_tailcall_mem_clobber, .-bad_indirect_tailcall_mem_clobber + + .globl bad_indirect_tailcall_mem_chain_of_auts + .type bad_indirect_tailcall_mem_chain_of_auts,@function +bad_indirect_tailcall_mem_chain_of_auts: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_chain_of_auts, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x16] +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: autda x16, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x16] +// CHECK-NEXT: {{[0-9a-f]+}}: br x16 + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it. + br x16 + .size bad_indirect_tailcall_mem_chain_of_auts, .-bad_indirect_tailcall_mem_chain_of_auts + + .globl bad_indirect_tailcall_mem_multi_bb + .type bad_indirect_tailcall_mem_multi_bb,@function +bad_indirect_tailcall_mem_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0] + ldr x16, [x0] + cbz x2, 1f + autia x16, x1 +1: + br x16 + .size bad_indirect_tailcall_mem_multi_bb, .-bad_indirect_tailcall_mem_multi_bb + + .globl bad_indirect_tailcall_mem_clobber_multi_bb + .type bad_indirect_tailcall_mem_clobber_multi_bb,@function +bad_indirect_tailcall_mem_clobber_multi_bb: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_clobber_multi_bb, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w16, w2 + ldr x16, [x0] + autia x16, x0 + cbz x2, 1f + mov w16, w2 +1: + br x16 + .size bad_indirect_tailcall_mem_clobber_multi_bb, .-bad_indirect_tailcall_mem_clobber_multi_bb + +// Test that calling a function is considered as invalidating safety of every +// register. Note that we only have to consider "returning" function calls +// (via branch-with-link), but both direct and indirect variants. +// Checking different registers: +// * x2 - function argument +// * x8 - indirect result location +// * x10 - temporary +// * x16 - intra-procedure-call scratch register +// * x18 - platform register +// * x20 - callee-saved register + + .globl direct_call_invalidates_safety + .type direct_call_invalidates_safety,@function +direct_call_invalidates_safety: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x8 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x10 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x18 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x20 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + mov x2, x0 + autiza x2 + bl callee + blr x2 + + mov x8, x0 + autiza x8 + bl callee + blr x8 + + mov x10, x0 + autiza x10 + bl callee + blr x10 + + mov x16, x0 + autiza x16 + bl callee + blr x16 + + mov x18, x0 + autiza x18 + bl callee + blr x18 + + mov x20, x0 + autiza x20 + bl callee + blr x20 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size direct_call_invalidates_safety, .-direct_call_invalidates_safety + + .globl indirect_call_invalidates_safety + .type indirect_call_invalidates_safety,@function +indirect_call_invalidates_safety: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x2 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x2 +// Check that only one error is reported per pair of BLRs. +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x2 + +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x8 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x8 +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x8 + +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x10 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x10 +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x10 + +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x16 +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x16 + +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x18 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x18 +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x18 + +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x20 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blr x20 +// CHECK-NOT: The instruction is {{[0-9a-f]+}}: blr x20 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + mov x2, x0 + autiza x2 + blr x2 // protected call, but makes x2 unsafe + blr x2 // unprotected call + + mov x8, x0 + autiza x8 + blr x8 // protected call, but makes x8 unsafe + blr x8 // unprotected call + + mov x10, x0 + autiza x10 + blr x10 // protected call, but makes x10 unsafe + blr x10 // unprotected call + + mov x16, x0 + autiza x16 + blr x16 // protected call, but makes x16 unsafe + blr x16 // unprotected call + + mov x18, x0 + autiza x18 + blr x18 // protected call, but makes x18 unsafe + blr x18 // unprotected call + + mov x20, x0 + autiza x20 + blr x20 // protected call, but makes x20 unsafe + blr x20 // unprotected call + + ldp x29, x30, [sp], #16 + autiasp + ret + .size indirect_call_invalidates_safety, .-indirect_call_invalidates_safety + +// Test that fused auth+use Armv8.3 instruction do not mark register as safe. + + .globl blraa_no_mark_safe + .type blraa_no_mark_safe,@function +blraa_no_mark_safe: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function blraa_no_mark_safe, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: blraa x0, x1 +// 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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: blraa x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + blraa x0, x1 // safe, no write-back, clobbers everything + blr x0 // unsafe + + ldp x29, x30, [sp], #16 + autiasp + ret + .size blraa_no_mark_safe, .-blraa_no_mark_safe + +// Check that the correct set of registers is used to compute the set of last +// writing instructions: both x16 and x17 are tracked in this function, but +// only one particular register is used to compute the set of clobbering +// instructions in each report. + + .globl last_insts_writing_to_reg + .type last_insts_writing_to_reg,@function +last_insts_writing_to_reg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x17, [x1] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x17 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret +// CHECK-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: blr x17 +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x17, [x1] +// 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]+}}: mov x29, sp +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x17, [x1] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x17 +// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + blr x16 + ldr x17, [x1] + blr x17 + + ldp x29, x30, [sp], #16 + autiasp + ret + .size last_insts_writing_to_reg, .-last_insts_writing_to_reg + + .globl main + .type main,@function +main: + mov x0, 0 + ret + .size main, .-main diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s index 30b70b060b94b..b271cda9da62f 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s @@ -3,6 +3,8 @@ // RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe // RUN: llvm-bolt-binary-analysis --scanners=pacret -no-threads \ // RUN: -debug-only bolt-pauth-scanner %t.exe 2>&1 | FileCheck %s +// RUN: llvm-bolt-binary-analysis --scanners=pauth -no-threads \ +// RUN: -debug-only bolt-pauth-scanner %t.exe 2>&1 | FileCheck -check-prefixes=CHECK,PAUTH %s // Check the debug output generated by PAuth gadget scanner to make sure the // that output is kept meaningful and to provide an overview of what happens @@ -12,8 +14,12 @@ .type simple,@function simple: paciasp + stp x29, x30, [sp, #-0x10]! b 1f 1: + autiza x0 + blr x0 + ldp x29, x30, [sp], #0x10 autiasp ret .size simple, .-simple @@ -25,16 +31,20 @@ simple: // ... // CHECK: BB Layout : [[BB0:[0-9a-zA-Z.]+]], [[BB1:[0-9a-zA-Z.]+]] // CHECK-NEXT: } -// CHECK-NEXT: [[BB0]] (2 instructions, align : 1) +// CHECK-NEXT: [[BB0]] (3 instructions, align : 1) // CHECK-NEXT: Entry Point // CHECK-NEXT: 00000000: paciasp -// CHECK-NEXT: 00000004: b [[BB1]] +// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! +// CHECK-NEXT: 00000008: b [[BB1]] // CHECK-NEXT: Successors: [[BB1]] // CHECK-EMPTY: -// CHECK-NEXT: [[BB1]] (2 instructions, align : 1) +// CHECK-NEXT: [[BB1]] (5 instructions, align : 1) // CHECK-NEXT: Predecessors: [[BB0]] -// CHECK-NEXT: 00000008: autiasp -// CHECK-NEXT: 0000000c: ret +// CHECK-NEXT: 0000000c: autiza x0 +// CHECK-NEXT: 00000010: blr x0 +// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 +// CHECK-NEXT: 00000018: autiasp +// CHECK-NEXT: 0000001c: ret // CHECK-EMPTY: // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: @@ -42,12 +52,20 @@ simple: // CHECK-EMPTY: // CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #25, pacret-state) // CHECK-NEXT: .. result: (pacret-state) +// CHECK-NEXT: PacRetAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::ComputeNext( b [[BB1]], pacret-state) // CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::Confluence( // CHECK-NEXT: State 1: pacret-state // CHECK-NEXT: State 2: pacret-state) // CHECK-NEXT: merged state: pacret-state +// CHECK-NEXT: PacRetAnalysis::ComputeNext( autiza x0, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) +// CHECK-NEXT: PacRetAnalysis::ComputeNext( blr x0, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) +// CHECK-NEXT: PacRetAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state) // CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state) @@ -56,6 +74,12 @@ simple: // CHECK-NEXT: State 1: pacret-state // CHECK-NEXT: State 2: pacret-state) // CHECK-NEXT: merged state: pacret-state +// CHECK-NEXT: PacRetAnalysis::ComputeNext( autiza x0, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) +// CHECK-NEXT: PacRetAnalysis::ComputeNext( blr x0, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) +// CHECK-NEXT: PacRetAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, pacret-state) +// CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state) // CHECK-NEXT: .. result: (pacret-state) // CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state) @@ -67,21 +91,28 @@ simple: // ... // CHECK: BB Layout : [[BB0]], [[BB1]] // CHECK-NEXT: } -// CHECK-NEXT: [[BB0]] (2 instructions, align : 1) +// CHECK-NEXT: [[BB0]] (3 instructions, align : 1) // CHECK-NEXT: Entry Point // CHECK-NEXT: 00000000: paciasp # PacRetAnalysis: pacret-state -// CHECK-NEXT: 00000004: b [[BB1]] # PacRetAnalysis: pacret-state +// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # PacRetAnalysis: pacret-state +// CHECK-NEXT: 00000008: b [[BB1]] # PacRetAnalysis: pacret-state // CHECK-NEXT: Successors: [[BB1]] // CHECK-EMPTY: -// CHECK-NEXT: [[BB1]] (2 instructions, align : 1) +// CHECK-NEXT: [[BB1]] (5 instructions, align : 1) // CHECK-NEXT: Predecessors: [[BB0]] -// CHECK-NEXT: 00000008: autiasp # PacRetAnalysis: pacret-state -// CHECK-NEXT: 0000000c: ret # PacRetAnalysis: pacret-state +// CHECK-NEXT: 0000000c: autiza x0 # PacRetAnalysis: pacret-state +// CHECK-NEXT: 00000010: blr x0 # PacRetAnalysis: pacret-state +// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # PacRetAnalysis: pacret-state +// CHECK-NEXT: 00000018: autiasp # PacRetAnalysis: pacret-state +// CHECK-NEXT: 0000001c: ret # PacRetAnalysis: pacret-state // CHECK-EMPTY: // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: // CHECK-NEXT: End of Function "simple" // CHECK-EMPTY: +// PAUTH-NEXT: Found call inst: 00000000: blr x0 # PacRetAnalysis: pacret-state +// PAUTH-NEXT: Call destination reg: X0 +// PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}} // CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none)