[BOLT] Gadget scanner: detect non-protected indirect calls (#131899)

Implement the detection of non-protected indirect calls and branches
similar to pac-ret scanner.
This commit is contained in:
Anatoly Trosinenko
2025-04-03 16:40:34 +03:00
committed by GitHub
parent ae8ad8649d
commit c818ae7399
9 changed files with 923 additions and 31 deletions

View File

@@ -577,6 +577,16 @@ public:
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 {

View File

@@ -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"; }

View File

@@ -81,9 +81,9 @@ extern llvm::cl::opt<unsigned> 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<GadgetScannerKind> GadgetScannersToRun;
extern llvm::cl::bits<GadgetScannerKind> GadgetScannersToRun;
} // namespace opts

View File

@@ -401,11 +401,11 @@ protected:
public:
std::vector<MCInstReference>
getLastClobberingInsts(const MCInst Ret, BinaryFunction &BF,
const ArrayRef<MCPhysReg> UsedDirtyRegs) const {
getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF,
const ArrayRef<MCPhysReg> 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<GadgetReport>(RetKind, Inst, RetReg);
}
static std::shared_ptr<Report>
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<GadgetReport>(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;

View File

@@ -247,12 +247,14 @@ static cl::opt<bool> WriteBoltInfoSection(
"bolt-info", cl::desc("write bolt info section in the output binary"),
cl::init(true), cl::Hidden, cl::cat(BoltOutputCategory));
cl::list<GadgetScannerKind>
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<GadgetScannerKind> 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<PAuthGadgetScanner::Analysis>());
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<PAuthScanner>(/*OnlyPacRetChecks=*/false));
} else if (RunAll || opts::GadgetScannersToRun.isSet(GSK::GS_PACRET)) {
Manager.registerPass(
std::make_unique<PAuthScanner>(/*OnlyPacRetChecks=*/true));
}
BC->logBOLTErrorsAndQuitOnFatal(Manager.runPasses());

View File

@@ -277,6 +277,33 @@ public:
}
}
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;
}

View File

@@ -33,7 +33,8 @@ HELP-EMPTY:
HELP-NEXT: BinaryAnalysis options:
HELP-EMPTY:
HELP-NEXT: --scanners=<value> - 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:

View File

@@ -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

View File

@@ -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: <empty>
@@ -42,12 +52,20 @@ simple:
// CHECK-EMPTY:
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #25, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( stp x29, x30, [sp, #-0x10]!, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( b [[BB1]], pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::Confluence(
// CHECK-NEXT: State 1: pacret-state<empty>
// CHECK-NEXT: State 2: pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: merged state: pacret-state<SafeToDerefRegs: , Insts: >
// CHECK-NEXT: PacRetAnalysis::ComputeNext( autiza x0, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( blr x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
@@ -56,6 +74,12 @@ simple:
// CHECK-NEXT: State 1: pacret-state<SafeToDerefRegs: , Insts: >
// CHECK-NEXT: State 2: pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: merged state: pacret-state<SafeToDerefRegs: , Insts: >
// CHECK-NEXT: PacRetAnalysis::ComputeNext( autiza x0, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( blr x0, pacret-state<SafeToDerefRegs: W0 X0 W0_HI , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ldp x29, x30, [sp], #0x10, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( hint #29, pacret-state<SafeToDerefRegs: , Insts: >)
// CHECK-NEXT: .. result: (pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
// CHECK-NEXT: PacRetAnalysis::ComputeNext( ret x30, pacret-state<SafeToDerefRegs: LR W30 W30_HI , Insts: >)
@@ -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<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000004: b [[BB1]] # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000008: b [[BB1]] # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// 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<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 0000000c: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 0000000c: autiza x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000010: blr x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 00000018: autiasp # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: 0000001c: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-EMPTY:
// CHECK-NEXT: DWARF CFI Instructions:
// CHECK-NEXT: <empty>
// CHECK-NEXT: End of Function "simple"
// CHECK-EMPTY:
// PAUTH-NEXT: Found call inst: 00000000: blr x0 # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// PAUTH-NEXT: Call destination reg: X0
// PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}}
// CHECK-NEXT: Found RET inst: 00000000: ret # PacRetAnalysis: pacret-state<SafeToDerefRegs: BitVector, Insts: >
// CHECK-NEXT: RetReg: LR
// CHECK-NEXT: Authenticated reg: (none)