https://github.com/atrosinenko created https://github.com/llvm/llvm-project/pull/131899
None >From 51bdcad07eafa20c38215a7426b81831d60651cd Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <atrosine...@accesssoftek.com> Date: Tue, 18 Mar 2025 21:32:11 +0300 Subject: [PATCH] [BOLT] Gadget scanner: detect non-protected indirect calls --- bolt/include/bolt/Core/MCPlusBuilder.h | 10 + bolt/lib/Passes/PAuthGadgetScanner.cpp | 33 +- .../Target/AArch64/AArch64MCPlusBuilder.cpp | 42 ++ .../binary-analysis/AArch64/gs-pauth-calls.s | 676 ++++++++++++++++++ 4 files changed, 757 insertions(+), 4 deletions(-) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-calls.s diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 76ea2489e7038..b3d54ccd5955d 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 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/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index ebfa606ceb7c3..bdaf362811662 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -382,11 +382,11 @@ class PacRetAnalysis 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; @@ -434,6 +434,29 @@ static std::shared_ptr<Report> tryCheckReturn(const BinaryContext &BC, return std::make_shared<GadgetReport>(RetKind, Inst, RetReg); } +static std::shared_ptr<Report> tryCheckCall(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::computeDfState(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) { @@ -450,10 +473,12 @@ Analysis::computeDfState(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 (auto Report = tryCheckReturn(BC, Inst, S)) Result.Diagnostics.push_back(Report); + if (auto Report = tryCheckCall(BC, Inst, S)) + Result.Diagnostics.push_back(Report); } } diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index d238a1df5c7d7..9ce1514639f95 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -277,6 +277,48 @@ 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::B: + case AArch64::BL: + assert(Inst.getOperand(0).isExpr()); + return getNoRegister(); + case AArch64::Bcc: + case AArch64::CBNZW: + case AArch64::CBNZX: + case AArch64::CBZW: + case AArch64::CBZX: + assert(Inst.getOperand(1).isExpr()); + return getNoRegister(); + case AArch64::TBNZW: + case AArch64::TBNZX: + case AArch64::TBZW: + case AArch64::TBZX: + assert(Inst.getOperand(2).isExpr()); + return getNoRegister(); + 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: + llvm_unreachable("Unhandled call instruction"); + } + } + bool isADRP(const MCInst &Inst) const override { return Inst.getOpcode() == AArch64::ADRP; } 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..3098e8ec954d2 --- /dev/null +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s @@ -0,0 +1,676 @@ +// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe +// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck %s + +// FIXME In the below test cases, LR is usually not spilled as needed, as it is +// not checked by BOLT. + + .text + + .globl good_direct_call + .type good_direct_call,@function +good_direct_call: +// CHECK-NOT: good_direct_call + paciasp + bl callee_ext + 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 + autia x0, x1 + blr x0 + 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 + ldr x16, [x0] + autia x16, x0 + blr x16 + 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 + blraa x0, x1 + 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 + ldr x16, [x0] + blraa x16, x0 + 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 + blr x0 + 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]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + ldr x16, [x0] + blr x16 + 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]+}}: autia x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: mov w0, w2 +// CHECK-NEXT: {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + autia x0, x1 + mov w0, w2 + blr x0 + 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]+}}: 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]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + ldr x16, [x0] + autia x16, x0 + mov w16, w2 + blr x16 + 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 + ldr x16, [x0] + autia x16, x1 + ldr x16, [x16] + autia x16, x0 + blr x16 + 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]+}}: ldr x16, [x0] +// CHECK-NEXT: {{[0-9a-f]+}}: autia x16, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x16] +// CHECK-NEXT: {{[0-9a-f]+}}: blr x16 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + ldr x16, [x0] + autia x16, x1 + ldr x16, [x16] + // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it. + blr x16 + 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 + autia x0, x1 + cbz x2, 1f + blr x0 +1: + ldr x1, [x0] + 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 + ldr x16, [x0] + autia x16, x0 + cbz x2, 1f + blr x16 +1: + ldr w0, [x16] + 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 + cbz x2, 1f + autia x0, x1 +1: + blr x0 + 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 + ldr x16, [x0] + cbz x2, 1f + autia x16, x1 +1: + blr x16 + 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 + autia x0, x1 + cbz x2, 1f + mov w0, w3 +1: + blr x0 + 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 + ldr x16, [x0] + autia x16, x0 + cbz x2, 1f + mov w16, w2 +1: + blr x16 + 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 + ldr x16, [x0] + autia x16, x1 + ldr x16, [x16] + autia x16, x0 + cbz x2, 1f + blr x16 +1: + ldr w0, [x16] + 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 + ldr x16, [x0] + autia x16, x1 + ldr x16, [x16] + cbz x2, 1f + autia x16, x0 +1: + blr x16 + autiasp + ret + .size bad_indirect_call_mem_chain_of_auts_multi_bb, .-bad_indirect_call_mem_chain_of_auts_multi_bb + +// Test that noreturn function calls via BR are checked as well. + + .globl good_indirect_noreturn_call + .type good_indirect_noreturn_call,@function +good_indirect_noreturn_call: +// CHECK-NOT: good_indirect_noreturn_call + paciasp + cbz x0, 2f + autiasp + ldr w1, [x30] + autia x0, x1 + br x0 +2: + autiasp + ret + .size good_indirect_noreturn_call, .-good_indirect_noreturn_call + + .globl bad_indirect_noreturn_call + .type bad_indirect_noreturn_call,@function +bad_indirect_noreturn_call: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_noreturn_call, basic block .LFT{{[0-9]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + cbz x0, 2f + br x0 +2: + autiasp + ret + .size bad_indirect_noreturn_call, .-bad_indirect_noreturn_call + +// 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_ext + .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]+}}: autia x16, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x16, [x16] +// CHECK-NEXT: {{[0-9a-f]+}}: br x16 + ldr x16, [x0] + autia 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 +// FIXME: Print the destination of BL as callee_ext instead of .LtmpN +// 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 +// 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 +// 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 +// 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 +// 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 + + paciasp + + mov x2, x0 + autiza x2 + bl callee_ext + blr x2 + + mov x8, x0 + autiza x8 + bl callee_ext + blr x8 + + mov x10, x0 + autiza x10 + bl callee_ext + blr x10 + + mov x16, x0 + autiza x16 + bl callee_ext + blr x16 + + mov x18, x0 + autiza x18 + bl callee_ext + blr x18 + + mov x20, x0 + autiza x20 + bl callee_ext + blr x20 + + 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 + + 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 x2 unsafe + blr x8 // unprotected call + + mov x10, x0 + autiza x10 + blr x10 // protected call, but makes x2 unsafe + blr x10 // unprotected call + + mov x16, x0 + autiza x16 + blr x16 // protected call, but makes x2 unsafe + blr x16 // unprotected call + + mov x19, x0 + autiza x18 + blr x18 // protected call, but makes x2 unsafe + blr x18 // unprotected call + + mov x20, x0 + autiza x20 + blr x20 // protected call, but makes x2 unsafe + blr x20 // unprotected call + + 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]+}}: blraa x0, x1 +// CHECK-NEXT: {{[0-9a-f]+}}: blr x0 +// CHECK-NEXT: {{[0-9a-f]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + blraa x0, x1 // safe, no write-back, clobbers everything + blr x0 // unsafe + 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. + + .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]+}}: 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]+}}: 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]+}}: 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]+}}: autiasp +// CHECK-NEXT: {{[0-9a-f]+}}: ret + paciasp + ldr x16, [x0] + blr x16 + ldr x17, [x1] + blr x17 + 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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits