[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:
committed by
GitHub
parent
ae8ad8649d
commit
c818ae7399
@@ -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 {
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
782
bolt/test/binary-analysis/AArch64/gs-pauth-calls.s
Normal file
782
bolt/test/binary-analysis/AArch64/gs-pauth-calls.s
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user