diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index 6f3b5923d3ef4..6b22ff62c555f 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -804,6 +804,19 @@ class BinaryFunction { return iterator_range(cie_begin(), cie_end()); } + /// Iterate over instructions (only if CFG is unavailable or not built yet). + iterator_range instrs() { + assert(!hasCFG() && "Iterate over basic blocks instead"); + return make_range(Instructions.begin(), Instructions.end()); + } + iterator_range instrs() const { + assert(!hasCFG() && "Iterate over basic blocks instead"); + return make_range(Instructions.begin(), Instructions.end()); + } + + /// Returns whether there are any labels at Offset. + bool hasLabelAt(unsigned Offset) const { return Labels.count(Offset) != 0; } + /// Iterate over all jump tables associated with this function. iterator_range::const_iterator> jumpTables() const { diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index 66251e195822c..75a8d26c64537 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -65,6 +65,14 @@ struct MCInstInBFReference { uint64_t Offset; MCInstInBFReference(BinaryFunction *BF, uint64_t Offset) : BF(BF), Offset(Offset) {} + + static MCInstInBFReference get(const MCInst *Inst, BinaryFunction &BF) { + for (auto &I : BF.instrs()) + if (Inst == &I.second) + return MCInstInBFReference(&BF, I.first); + return {}; + } + MCInstInBFReference() : BF(nullptr), Offset(0) {} bool operator==(const MCInstInBFReference &RHS) const { return BF == RHS.BF && Offset == RHS.Offset; @@ -104,6 +112,12 @@ struct MCInstReference { MCInstReference(BinaryFunction *BF, uint32_t Offset) : MCInstReference(MCInstInBFReference(BF, Offset)) {} + static MCInstReference get(const MCInst *Inst, BinaryFunction &BF) { + if (BF.hasCFG()) + return MCInstInBBReference::get(Inst, BF); + return MCInstInBFReference::get(Inst, BF); + } + bool operator<(const MCInstReference &RHS) const { if (ParentKind != RHS.ParentKind) return ParentKind < RHS.ParentKind; @@ -138,6 +152,16 @@ struct MCInstReference { llvm_unreachable(""); } + operator bool() const { + switch (ParentKind) { + case BasicBlockParent: + return U.BBRef.BB != nullptr; + case FunctionParent: + return U.BFRef.BF != nullptr; + } + llvm_unreachable(""); + } + uint64_t getAddress() const { switch (ParentKind) { case BasicBlockParent: diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 2a20bcd09c786..08e0e914501de 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -265,21 +265,30 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const { OS << ">"; } -class SrcSafetyAnalysis - : public DataflowAnalysis { - using Parent = - DataflowAnalysis; - friend Parent; - +/// Computes which registers are safe to be used by control flow instructions. +/// +/// This is the base class for two implementations: a dataflow-based analysis +/// which is intended to be used for most functions and a simplified CFG-unaware +/// version for functions without reconstructed CFG. +class SrcSafetyAnalysis { public: - SrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, + SrcSafetyAnalysis(BinaryFunction &BF, const std::vector &RegsToTrackInstsFor) - : Parent(BF, AllocId), NumRegs(BF.getBinaryContext().MRI->getNumRegs()), + : BC(BF.getBinaryContext()), NumRegs(BC.MRI->getNumRegs()), RegsToTrackInstsFor(RegsToTrackInstsFor) {} + virtual ~SrcSafetyAnalysis() {} + static std::shared_ptr + create(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, + const std::vector &RegsToTrackInstsFor); + + virtual void run() = 0; + virtual ErrorOr + getStateBefore(const MCInst &Inst) const = 0; + protected: + BinaryContext &BC; const unsigned NumRegs; /// RegToTrackInstsFor is the set of registers for which the dataflow analysis /// must compute which the last set of instructions writing to it are. @@ -296,8 +305,6 @@ class SrcSafetyAnalysis return S.LastInstWritingReg[Index]; } - void preflight() {} - SrcState createEntryState() { SrcState S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs()) @@ -305,36 +312,6 @@ class SrcSafetyAnalysis return S; } - SrcState getStartingStateAtBB(const BinaryBasicBlock &BB) { - if (BB.isEntryPoint()) - return createEntryState(); - - return SrcState(); - } - - SrcState getStartingStateAtPoint(const MCInst &Point) { return SrcState(); } - - void doConfluence(SrcState &StateOut, const SrcState &StateIn) { - SrcStatePrinter P(BC); - LLVM_DEBUG({ - dbgs() << " SrcSafetyAnalysis::Confluence(\n"; - dbgs() << " State 1: "; - P.print(dbgs(), StateOut); - dbgs() << "\n"; - dbgs() << " State 2: "; - P.print(dbgs(), StateIn); - dbgs() << ")\n"; - }); - - StateOut.merge(StateIn); - - LLVM_DEBUG({ - dbgs() << " merged state: "; - P.print(dbgs(), StateOut); - dbgs() << "\n"; - }); - } - BitVector getClobberedRegs(const MCInst &Point) const { BitVector Clobbered(NumRegs, false); // Assume a call can clobber all registers, including callee-saved @@ -438,8 +415,6 @@ class SrcSafetyAnalysis return Next; } - StringRef getAnnotationName() const { return StringRef("SrcSafetyAnalysis"); } - public: std::vector getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF, @@ -458,14 +433,178 @@ class SrcSafetyAnalysis } std::vector Result; for (const MCInst *Inst : LastWritingInsts) { - MCInstInBBReference Ref = MCInstInBBReference::get(Inst, BF); - assert(Ref.BB != nullptr && "Expected Inst to be found"); + MCInstReference Ref = MCInstReference::get(Inst, BF); + assert(Ref && "Expected Inst to be found"); Result.push_back(MCInstReference(Ref)); } return Result; } }; +class DataflowSrcSafetyAnalysis + : public SrcSafetyAnalysis, + public DataflowAnalysis { + using DFParent = DataflowAnalysis; + friend DFParent; + + using SrcSafetyAnalysis::BC; + using SrcSafetyAnalysis::computeNext; + +public: + DataflowSrcSafetyAnalysis(BinaryFunction &BF, + MCPlusBuilder::AllocatorIdTy AllocId, + const std::vector &RegsToTrackInstsFor) + : SrcSafetyAnalysis(BF, RegsToTrackInstsFor), DFParent(BF, AllocId) {} + + ErrorOr getStateBefore(const MCInst &Inst) const override { + return DFParent::getStateBefore(Inst); + } + + void run() override { DFParent::run(); } + +protected: + void preflight() {} + + SrcState getStartingStateAtBB(const BinaryBasicBlock &BB) { + if (BB.isEntryPoint()) + return createEntryState(); + + return SrcState(); + } + + SrcState getStartingStateAtPoint(const MCInst &Point) { return SrcState(); } + + void doConfluence(SrcState &StateOut, const SrcState &StateIn) { + SrcStatePrinter P(BC); + LLVM_DEBUG({ + dbgs() << " DataflowSrcSafetyAnalysis::Confluence(\n"; + dbgs() << " State 1: "; + P.print(dbgs(), StateOut); + dbgs() << "\n"; + dbgs() << " State 2: "; + P.print(dbgs(), StateIn); + dbgs() << ")\n"; + }); + + StateOut.merge(StateIn); + + LLVM_DEBUG({ + dbgs() << " merged state: "; + P.print(dbgs(), StateOut); + dbgs() << "\n"; + }); + } + + StringRef getAnnotationName() const { return "DataflowSrcSafetyAnalysis"; } +}; + +// A simplified implementation of DataflowSrcSafetyAnalysis for functions +// lacking CFG information. +// +// Let assume the instructions can only be executed linearly unless there is +// a label to jump to - this should handle both directly jumping to a location +// encoded as an immediate operand of a branch instruction, as well as saving a +// branch destination somewhere and passing it to an indirect branch instruction +// later, provided no arithmetic is performed on the destination address: +// +// ; good: the destination is directly encoded into the branch instruction +// cbz x0, some_label +// +// ; good: the branch destination is first stored and then used as-is +// adr x1, some_label +// br x1 +// +// ; bad: some clever arithmetic is performed manually +// adr x1, some_label +// add x1, x1, #4 +// br x1 +// ... +// some_label: +// ; pessimistically reset the state as we are unsure where we came from +// ... +// ret +// JTI0: +// .byte some_label - Ltmp0 ; computing offsets using labels may probably +// work too, provided enough information is +// retained by the assembler and linker +// +// Then, a function can be split into a number of disjoint contiguous sequences +// of instructions without labels in between. These sequences can be processed +// the same way basic blocks are processed by data-flow analysis, assuming +// pessimistically that all registers are unsafe at the start of each sequence. +class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { + BinaryFunction &BF; + MCPlusBuilder::AllocatorIdTy AllocId; + unsigned StateAnnotationIndex; + + void cleanStateAnnotations() { + for (auto &I : BF.instrs()) + BC.MIB->removeAnnotation(I.second, StateAnnotationIndex); + } + + /// Creates a state with all registers marked unsafe (not to be confused + /// with empty state). + SrcState createUnsafeState() const { + return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); + } + +public: + CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF, + MCPlusBuilder::AllocatorIdTy AllocId, + const std::vector &RegsToTrackInstsFor) + : SrcSafetyAnalysis(BF, RegsToTrackInstsFor), BF(BF), AllocId(AllocId) { + StateAnnotationIndex = + BC.MIB->getOrCreateAnnotationIndex("CFGUnawareSrcSafetyAnalysis"); + } + + void run() override { + SrcState S = createEntryState(); + for (auto &I : BF.instrs()) { + MCInst &Inst = I.second; + + // If there is a label before this instruction, it is possible that it + // can be jumped-to, thus conservatively resetting S. As an exception, + // let's ignore any labels at the beginning of the function, as at least + // one label is expected there. + if (BF.hasLabelAt(I.first) && &Inst != &BF.instrs().begin()->second) { + LLVM_DEBUG({ + traceInst(BC, "Due to label, resetting the state before", Inst); + }); + S = createUnsafeState(); + } + + // Check if we need to remove an old annotation (this is the case if + // this is the second, detailed, run of the analysis). + if (BC.MIB->hasAnnotation(Inst, StateAnnotationIndex)) + BC.MIB->removeAnnotation(Inst, StateAnnotationIndex); + // Attach the state *before* this instruction executes. + BC.MIB->addAnnotation(Inst, StateAnnotationIndex, S, AllocId); + + // Compute the state after this instruction executes. + S = computeNext(Inst, S); + } + } + + ErrorOr getStateBefore(const MCInst &Inst) const override { + return BC.MIB->getAnnotationAs(Inst, StateAnnotationIndex); + } + + ~CFGUnawareSrcSafetyAnalysis() { cleanStateAnnotations(); } +}; + +std::shared_ptr +SrcSafetyAnalysis::create(BinaryFunction &BF, + MCPlusBuilder::AllocatorIdTy AllocId, + const std::vector &RegsToTrackInstsFor) { + if (BF.hasCFG()) + return std::make_shared(BF, AllocId, + RegsToTrackInstsFor); + return std::make_shared(BF, AllocId, + RegsToTrackInstsFor); +} + static std::shared_ptr shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst, const SrcState &S) { @@ -519,43 +658,51 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst, return std::make_shared(CallKind, Inst, DestReg); } +template static void iterateOverInstrs(BinaryFunction &BF, T Fn) { + if (BF.hasCFG()) { + for (BinaryBasicBlock &BB : BF) + for (int64_t I = 0, E = BB.size(); I < E; ++I) + Fn(MCInstInBBReference(&BB, I)); + } else { + for (auto I : BF.instrs()) + Fn(MCInstInBFReference(&BF, I.first)); + } +} + FunctionAnalysisResult Analysis::findGadgets(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) { FunctionAnalysisResult Result; - SrcSafetyAnalysis PRA(BF, AllocatorId, {}); + auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, {}); LLVM_DEBUG({ dbgs() << "Running src register safety analysis...\n"; }); - PRA.run(); + Analysis->run(); LLVM_DEBUG({ dbgs() << "After src register safety analysis:\n"; BF.dump(); }); BinaryContext &BC = BF.getBinaryContext(); - for (BinaryBasicBlock &BB : BF) { - for (int64_t I = 0, E = BB.size(); I < E; ++I) { - MCInstReference Inst(&BB, I); - const SrcState &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. - if (S.empty()) { - Result.Diagnostics.push_back(std::make_shared( - Inst, "Warning: unreachable instruction found")); - continue; - } + iterateOverInstrs(BF, [&](MCInstReference Inst) { + const SrcState &S = *Analysis->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. + if (S.empty()) { + Result.Diagnostics.push_back(std::make_shared( + Inst, "Warning: unreachable instruction found")); + return; + } - if (auto Report = shouldReportReturnGadget(BC, Inst, S)) - Result.Diagnostics.push_back(Report); + if (auto Report = shouldReportReturnGadget(BC, Inst, S)) + Result.Diagnostics.push_back(Report); - if (PacRetGadgetsOnly) - continue; + if (PacRetGadgetsOnly) + return; - if (auto Report = shouldReportCallGadget(BC, Inst, S)) - Result.Diagnostics.push_back(Report); - } - } + if (auto Report = shouldReportCallGadget(BC, Inst, S)) + Result.Diagnostics.push_back(Report); + }); return Result; } @@ -571,10 +718,10 @@ void Analysis::computeDetailedInfo(BinaryFunction &BF, std::vector RegsToTrackVec(RegsToTrack.begin(), RegsToTrack.end()); // Re-compute the analysis with register tracking. - SrcSafetyAnalysis PRWIA(BF, AllocatorId, RegsToTrackVec); + auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, RegsToTrackVec); LLVM_DEBUG( { dbgs() << "\nRunning detailed src register safety analysis...\n"; }); - PRWIA.run(); + Analysis->run(); LLVM_DEBUG({ dbgs() << "After detailed src register safety analysis:\n"; BF.dump(); @@ -585,7 +732,7 @@ void Analysis::computeDetailedInfo(BinaryFunction &BF, LLVM_DEBUG( { traceInst(BC, "Attaching clobbering info to", Report->Location); }); (void)BC; - Report->setOverwritingInstrs(PRWIA.getLastClobberingInsts( + Report->setOverwritingInstrs(Analysis->getLastClobberingInsts( Report->Location, BF, Report->getAffectedRegisters())); } } @@ -598,9 +745,6 @@ void Analysis::runOnFunction(BinaryFunction &BF, BF.dump(); }); - if (!BF.hasCFG()) - return; - FunctionAnalysisResult FAR = findGadgets(BF, AllocatorId); if (FAR.Diagnostics.empty()) return; @@ -686,8 +830,11 @@ void GadgetReport::generateReport(raw_ostream &OS, }; if (OverwritingInstrs.size() == 1) { const MCInstReference OverwInst = OverwritingInstrs[0]; - assert(OverwInst.ParentKind == MCInstReference::BasicBlockParent); - reportFoundGadgetInSingleBBSingleOverwInst(OS, BC, OverwInst, Location); + // Printing the details for the MCInstReference::FunctionParent case + // is not implemented not to overcomplicate the code, as most functions + // are expected to have CFG information. + if (OverwInst.ParentKind == MCInstReference::BasicBlockParent) + reportFoundGadgetInSingleBBSingleOverwInst(OS, BC, OverwInst, Location); } } diff --git a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s index d506ec13f4895..2193d40131478 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s +++ b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s @@ -223,6 +223,21 @@ f_unreachable_instruction: ret .size f_unreachable_instruction, .-f_unreachable_instruction +// Expected false positive: without CFG, the state is reset to all-unsafe +// after an unconditional branch. + + .globl state_is_reset_after_indirect_branch_nocfg + .type state_is_reset_after_indirect_branch_nocfg,@function +state_is_reset_after_indirect_branch_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected ret found in function state_is_reset_after_indirect_branch_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: ret +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + adr x2, 1f + br x2 +1: + ret + .size state_is_reset_after_indirect_branch_nocfg, .-state_is_reset_after_indirect_branch_nocfg + /// Now do a basic sanity check on every different Authentication instruction: .globl f_autiasp diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s index 0f6c850583dda..0b6bd5509a42a 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-calls.s @@ -429,6 +429,324 @@ bad_indirect_call_mem_chain_of_auts_multi_bb: ret .size bad_indirect_call_mem_chain_of_auts_multi_bb, .-bad_indirect_call_mem_chain_of_auts_multi_bb +// Tests for CFG-unaware analysis. +// +// All these tests use an instruction sequence like this +// +// adr x2, 1f +// br x2 +// 1: +// ; ... +// +// to make BOLT unable to reconstruct the control flow. Note that one can easily +// tell whether the report corresponds to a function with or without CFG: +// normally, the location of the gadget is described like this: +// +// ... found in function , basic block , at address
+// +// When CFG information is not available, this is reduced to +// +// ... found in function , at address
+ + .globl good_direct_call_nocfg + .type good_direct_call_nocfg,@function +good_direct_call_nocfg: +// CHECK-NOT: good_direct_call_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + bl callee + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_direct_call_nocfg, .-good_direct_call_nocfg + + .globl good_indirect_call_arg_nocfg + .type good_indirect_call_arg_nocfg,@function +good_indirect_call_arg_nocfg: +// CHECK-NOT: good_indirect_call_arg_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + blr x0 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_arg_nocfg, .-good_indirect_call_arg_nocfg + + .globl good_indirect_call_mem_nocfg + .type good_indirect_call_mem_nocfg,@function +good_indirect_call_mem_nocfg: +// CHECK-NOT: good_indirect_call_mem_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autia x16, x0 + blr x16 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_nocfg, .-good_indirect_call_mem_nocfg + + .globl good_indirect_call_arg_v83_nocfg + .type good_indirect_call_arg_v83_nocfg,@function +good_indirect_call_arg_v83_nocfg: +// CHECK-NOT: good_indirect_call_arg_v83_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + blraa x0, x1 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_arg_v83_nocfg, .-good_indirect_call_arg_v83_nocfg + + .globl good_indirect_call_mem_v83_nocfg + .type good_indirect_call_mem_v83_nocfg,@function +good_indirect_call_mem_v83_nocfg: +// CHECK-NOT: good_indirect_call_mem_v83_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + blraa x16, x0 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_v83_nocfg, .-good_indirect_call_mem_v83_nocfg + + .globl bad_indirect_call_arg_nocfg + .type bad_indirect_call_arg_nocfg,@function +bad_indirect_call_arg_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_nocfg, 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 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg_nocfg, .-bad_indirect_call_arg_nocfg + + .globl obscure_indirect_call_arg_nocfg + .type obscure_indirect_call_arg_nocfg,@function +obscure_indirect_call_arg_nocfg: +// CHECK-NOCFG-LABEL: GS-PAUTH: non-protected call found in function obscure_indirect_call_arg_nocfg, at address +// CHECK-NOCFG-NEXT: The instruction is {{[0-9a-f]+}}: blr x0 +// CHECK-NOCFG-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 // not observed by the checker + b 1f +1: + // The register state is pessimistically reset after a label, thus + // the below branch instruction is reported as non-protected - this is + // a known false-positive. + blr x0 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size obscure_good_indirect_call_arg_nocfg, .-obscure_good_indirect_call_arg_nocfg + + .globl safe_lr_at_function_entry_nocfg + .type safe_lr_at_function_entry_nocfg,@function +safe_lr_at_function_entry_nocfg: +// CHECK-NOT: safe_lr_at_function_entry_nocfg + cbz x0, 1f + ret // LR is safe at the start of the function +1: + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + adr x2, 2f + br x2 +2: + ldp x29, x30, [sp], #16 + autiasp + ret + .size safe_lr_at_function_entry_nocfg, .-safe_lr_at_function_entry_nocfg + + .globl lr_is_never_unsafe_before_first_inst_nocfg + .type lr_is_never_unsafe_before_first_inst_nocfg,@function +// CHECK-NOT: lr_is_never_unsafe_before_first_inst_nocfg +lr_is_never_unsafe_before_first_inst_nocfg: +1: + // The register state is never reset before the first instruction of + // the function. This can lead to a known false-negative if LR is + // clobbered and then a jump to the very first instruction of the + // function is performed. + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + mov x30, x0 + cbz x1, 1b + + adr x2, 2f + br x2 +2: + ldp x29, x30, [sp], #16 + autiasp + ret + .size lr_is_never_unsafe_before_first_inst_nocfg, .-lr_is_never_unsafe_before_first_inst_nocfg + + .globl bad_indirect_call_mem_nocfg + .type bad_indirect_call_mem_nocfg,@function +bad_indirect_call_mem_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_nocfg, 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] + blr x16 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_nocfg, .-bad_indirect_call_mem_nocfg + + .globl bad_indirect_call_arg_clobber_nocfg + .type bad_indirect_call_arg_clobber_nocfg,@function +bad_indirect_call_arg_clobber_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_arg_clobber_nocfg, 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 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + autia x0, x1 + mov w0, w2 + blr x0 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_arg_clobber_nocfg, .-bad_indirect_call_arg_clobber_nocfg + + .globl bad_indirect_call_mem_clobber_nocfg + .type bad_indirect_call_mem_clobber_nocfg,@function +bad_indirect_call_mem_clobber_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_clobber_nocfg, 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 + mov w16, w2 + blr x16 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_clobber_nocfg, .-bad_indirect_call_mem_clobber_nocfg + + .globl good_indirect_call_mem_chain_of_auts_nocfg + .type good_indirect_call_mem_chain_of_auts_nocfg,@function +good_indirect_call_mem_chain_of_auts_nocfg: +// CHECK-NOT: good_indirect_call_mem_chain_of_auts_nocfg + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + autda x16, x1 + ldr x16, [x16] + autia x16, x0 + blr x16 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size good_indirect_call_mem_chain_of_auts_nocfg, .-good_indirect_call_mem_chain_of_auts_nocfg + + .globl bad_indirect_call_mem_chain_of_auts_nocfg + .type bad_indirect_call_mem_chain_of_auts_nocfg,@function +bad_indirect_call_mem_chain_of_auts_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_call_mem_chain_of_auts_nocfg, 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] + // Missing AUT of x16. The fact that x16 was authenticated above has nothing to do with it. + blr x16 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size bad_indirect_call_mem_chain_of_auts_nocfg, .-bad_indirect_call_mem_chain_of_auts_nocfg + // 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. @@ -537,6 +855,109 @@ bad_indirect_tailcall_mem_clobber_multi_bb: br x16 .size bad_indirect_tailcall_mem_clobber_multi_bb, .-bad_indirect_tailcall_mem_clobber_multi_bb + .globl good_direct_tailcall_nocfg + .type good_direct_tailcall_nocfg,@function +good_direct_tailcall_nocfg: +// CHECK-NOT: good_direct_tailcall_nocfg + adr x2, 1f + br x2 +1: + b callee + .size good_direct_tailcall_nocfg, .-good_direct_tailcall_nocfg + + .globl good_indirect_tailcall_mem_nocfg + .type good_indirect_tailcall_mem_nocfg,@function +good_indirect_tailcall_mem_nocfg: +// CHECK-NOT: good_indirect_tailcall_mem_nocfg + adr x2, 1f + br x2 +1: + ldr x16, [x0] + autia x16, x0 + br x16 + .size good_indirect_tailcall_mem_nocfg, .-good_indirect_tailcall_mem_nocfg + + .globl good_indirect_tailcall_mem_v83_nocfg + .type good_indirect_tailcall_mem_v83_nocfg,@function +good_indirect_tailcall_mem_v83_nocfg: +// CHECK-NOT: good_indirect_tailcall_mem_v83_nocfg + adr x2, 1f + br x2 +1: + ldr x16, [x0] + braa x16, x0 + .size good_indirect_tailcall_mem_v83_nocfg, .-good_indirect_tailcall_mem_v83_nocfg + + .globl bad_indirect_tailcall_mem_nocfg + .type bad_indirect_tailcall_mem_nocfg,@function +bad_indirect_tailcall_mem_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_nocfg, 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] + adr x2, 1f + br x2 +1: + ldr x16, [x0] + br x16 + .size bad_indirect_tailcall_mem_nocfg, .-bad_indirect_tailcall_mem_nocfg + + .globl bad_indirect_tailcall_mem_clobber_nocfg + .type bad_indirect_tailcall_mem_clobber_nocfg,@function +bad_indirect_tailcall_mem_clobber_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_clobber_nocfg, 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 + adr x2, 1f + br x2 +1: + ldr x16, [x0] + autia x16, x0 + mov w16, w2 + br x16 + .size bad_indirect_tailcall_mem_clobber_nocfg, .-bad_indirect_tailcall_mem_clobber_nocfg + + .globl bad_indirect_tailcall_mem_chain_of_auts_nocfg + .type bad_indirect_tailcall_mem_chain_of_auts_nocfg,@function +bad_indirect_tailcall_mem_chain_of_auts_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function bad_indirect_tailcall_mem_chain_of_auts_nocfg, 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] + adr x2, 1f + br x2 +1: + 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_nocfg, .-bad_indirect_tailcall_mem_chain_of_auts_nocfg + + .globl state_is_reset_at_branch_destination_nocfg + .type state_is_reset_at_branch_destination_nocfg,@function +state_is_reset_at_branch_destination_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function state_is_reset_at_branch_destination_nocfg, 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 + + b 1f + autia x0, x1 // skipped +1: + blr x0 + + adr x2, 2f + br x2 +2: + ldp x29, x30, [sp], #16 + autiasp + ret + .size state_is_reset_at_branch_destination_nocfg, .-state_is_reset_at_branch_destination_nocfg + // 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. @@ -692,6 +1113,156 @@ indirect_call_invalidates_safety: ret .size indirect_call_invalidates_safety, .-indirect_call_invalidates_safety + .globl direct_call_invalidates_safety_nocfg + .type direct_call_invalidates_safety_nocfg,@function +direct_call_invalidates_safety_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function direct_call_invalidates_safety_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size direct_call_invalidates_safety_nocfg, .-direct_call_invalidates_safety_nocfg + + .globl indirect_call_invalidates_safety_nocfg + .type indirect_call_invalidates_safety_nocfg,@function +indirect_call_invalidates_safety_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function indirect_call_invalidates_safety_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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_nocfg, 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 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size indirect_call_invalidates_safety_nocfg, .-indirect_call_invalidates_safety_nocfg + // Test that fused auth+use Armv8.3 instruction do not mark register as safe. .globl blraa_no_mark_safe @@ -722,6 +1293,28 @@ blraa_no_mark_safe: ret .size blraa_no_mark_safe, .-blraa_no_mark_safe + .globl blraa_no_mark_safe_nocfg + .type blraa_no_mark_safe_nocfg,@function +blraa_no_mark_safe_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function blraa_no_mark_safe_nocfg, 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 + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + blraa x0, x1 // safe, no write-back, clobbers everything + blr x0 // detected as unsafe + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size blraa_no_mark_safe_nocfg, .-blraa_no_mark_safe_nocfg + // 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 @@ -774,6 +1367,65 @@ last_insts_writing_to_reg: ret .size last_insts_writing_to_reg, .-last_insts_writing_to_reg + .globl last_insts_writing_to_reg_nocfg + .type last_insts_writing_to_reg_nocfg,@function +last_insts_writing_to_reg_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg_nocfg, 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-LABEL: GS-PAUTH: non-protected call found in function last_insts_writing_to_reg_nocfg, 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] + paciasp + stp x29, x30, [sp, #-16]! + mov x29, sp + + ldr x16, [x0] + blr x16 + ldr x17, [x1] + blr x17 + + adr x2, 1f + br x2 +1: + ldp x29, x30, [sp], #16 + autiasp + ret + .size last_insts_writing_to_reg_nocfg, .-last_insts_writing_to_reg_nocfg + +// Test that the instructions reported to the user are not cluttered with +// annotations attached by data-flow analysis or its CFG-unaware counterpart. + + .globl printed_instrs_dataflow + .type printed_instrs_dataflow,@function +printed_instrs_dataflow: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function printed_instrs_dataflow, basic block {{[^,]+}}, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # TAILCALL{{ *$}} +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}} +// CHECK-NEXT: This happens in the following basic block: +// CHECK-NEXT: {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}} +// CHECK-NEXT: {{[0-9a-f]+}}: br x0 # TAILCALL{{ *$}} + ldr x0, [x0] + br x0 + .size printed_instrs_dataflow, .-printed_instrs_dataflow + + .globl printed_instrs_nocfg + .type printed_instrs_nocfg,@function +printed_instrs_nocfg: +// CHECK-LABEL: GS-PAUTH: non-protected call found in function printed_instrs_nocfg, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x0 # UNKNOWN CONTROL FLOW # Offset: 12{{ *$}} +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldr x0, [x0]{{ *$}} + adr x2, 1f + br x2 +1: + ldr x0, [x0] + br x0 + .size printed_instrs_nocfg, .-printed_instrs_nocfg + .globl main .type main,@function main: diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s index cca1efe695168..e4a74bfe1e17a 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s @@ -57,7 +57,7 @@ simple: // CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( b [[BB1]], src-state) // CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::Confluence( +// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence( // CHECK-NEXT: State 1: src-state // CHECK-NEXT: State 2: src-state) // CHECK-NEXT: merged state: src-state @@ -71,7 +71,7 @@ simple: // CHECK-NEXT: .. result: (src-state) // CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) // CHECK-NEXT: .. result: (src-state) -// CHECK-NEXT: SrcSafetyAnalysis::Confluence( +// CHECK-NEXT: DataflowSrcSafetyAnalysis::Confluence( // CHECK-NEXT: State 1: src-state // CHECK-NEXT: State 2: src-state) // CHECK-NEXT: merged state: src-state @@ -94,27 +94,27 @@ simple: // CHECK-NEXT: } // CHECK-NEXT: [[BB0]] (3 instructions, align : 1) // CHECK-NEXT: Entry Point -// CHECK-NEXT: 00000000: paciasp # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000008: b [[BB1]] # SrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000000: paciasp # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: stp x29, x30, [sp, #-0x10]! # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000008: b [[BB1]] # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: Successors: [[BB1]] // CHECK-EMPTY: // CHECK-NEXT: [[BB1]] (5 instructions, align : 1) // CHECK-NEXT: Predecessors: [[BB0]] -// CHECK-NEXT: 0000000c: autiza x0 # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000010: blr x0 # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 00000018: autiasp # SrcSafetyAnalysis: src-state -// CHECK-NEXT: 0000001c: ret # SrcSafetyAnalysis: src-state +// CHECK-NEXT: 0000000c: autiza x0 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000010: blr x0 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000014: ldp x29, x30, [sp], #0x10 # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000018: autiasp # DataflowSrcSafetyAnalysis: src-state +// CHECK-NEXT: 0000001c: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-EMPTY: // CHECK-NEXT: DWARF CFI Instructions: // CHECK-NEXT: // CHECK-NEXT: End of Function "simple" // CHECK-EMPTY: -// PAUTH-NEXT: Found call inst: 00000000: blr x0 # SrcSafetyAnalysis: src-state +// PAUTH-NEXT: Found call inst: 00000000: blr x0 # DataflowSrcSafetyAnalysis: src-state // PAUTH-NEXT: Call destination reg: X0 // PAUTH-NEXT: SafeToDerefRegs: W0 X0 W0_HI{{[ \t]*$}} -// CHECK-NEXT: Found RET inst: 00000000: ret # SrcSafetyAnalysis: src-state +// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none) // CHECK-NEXT: SafeToDerefRegs: LR W30 W30_HI{{[ \t]*$}} @@ -141,7 +141,7 @@ clobber: // The above output was printed after first run of analysis // CHECK-EMPTY: -// CHECK-NEXT: Found RET inst: 00000000: ret # SrcSafetyAnalysis: src-state +// CHECK-NEXT: Found RET inst: 00000000: ret # DataflowSrcSafetyAnalysis: src-state // CHECK-NEXT: RetReg: LR // CHECK-NEXT: Authenticated reg: (none) // CHECK-NEXT: SafeToDerefRegs: W30_HI{{[ \t]*$}} @@ -160,8 +160,96 @@ clobber: // Iterating over the reports and attaching clobbering info: // CHECK-EMPTY: -// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # SrcSafetyAnalysis: src-state +// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # DataflowSrcSafetyAnalysis: src-state + .globl nocfg + .type nocfg,@function +nocfg: + adr x0, 1f + br x0 +1: + ret + .size nocfg, .-nocfg + +// CHECK-LABEL:Analyzing in function nocfg, AllocatorId 1 +// CHECK-NEXT: Binary Function "nocfg" { +// CHECK-NEXT: Number : 3 +// CHECK-NEXT: State : disassembled +// ... +// CHECK: IsSimple : 0 +// CHECK-NEXT: IsMultiEntry: 1 +// CHECK-NEXT: IsSplit : 0 +// CHECK-NEXT: BB Count : 0 +// CHECK-NEXT: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR:[0-9a-f]+]] +// CHECK-NEXT: } +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] +// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 +// CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point): +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000008: ret # Offset: 8 +// CHECK-NEXT: DWARF CFI Instructions: +// CHECK-NEXT: +// CHECK-NEXT: End of Function "nocfg" +// CHECK-EMPTY: +// CHECK-NEXT: Running src register safety analysis... +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8 +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: After src register safety analysis: +// CHECK-NEXT: Binary Function "nocfg" { +// CHECK-NEXT: Number : 3 +// CHECK-NEXT: State : disassembled +// ... +// CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]] +// CHECK-NEXT: } +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point): +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: DWARF CFI Instructions: +// CHECK-NEXT: +// CHECK-NEXT: End of Function "nocfg" +// CHECK-EMPTY: +// PAUTH-NEXT: Found call inst: 00000000: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// PAUTH-NEXT: Call destination reg: X0 +// PAUTH-NEXT: SafeToDerefRegs: LR W0 W30 X0 W0_HI W30_HI +// CHECK-NEXT: Found RET inst: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: RetReg: LR +// CHECK-NEXT: Authenticated reg: (none) +// CHECK-NEXT: SafeToDerefRegs: +// CHECK-EMPTY: +// CHECK-NEXT: Running detailed src register safety analysis... +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]], src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( br x0, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: Due to label, resetting the state before: 00000000: ret # Offset: 8 +// CHECK-NEXT: SrcSafetyAnalysis::ComputeNext( ret x30, src-state) +// CHECK-NEXT: .. result: (src-state) +// CHECK-NEXT: After detailed src register safety analysis: +// CHECK-NEXT: Binary Function "nocfg" { +// CHECK-NEXT: Number : 3 +// ... +// CHECK: Secondary Entry Points : __ENTRY_nocfg@0x[[ENTRY_ADDR]] +// CHECK-NEXT: } +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000000: adr x0, __ENTRY_nocfg@0x[[ENTRY_ADDR]] # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: 00000004: br x0 # UNKNOWN CONTROL FLOW # Offset: 4 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: __ENTRY_nocfg@0x[[ENTRY_ADDR]] (Entry Point): +// CHECK-NEXT: .{{[A-Za-z0-9]+}}: +// CHECK-NEXT: 00000008: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state +// CHECK-NEXT: DWARF CFI Instructions: +// CHECK-NEXT: +// CHECK-NEXT: End of Function "nocfg" +// CHECK-EMPTY: +// CHECK-NEXT: Attaching clobbering info to: 00000000: ret # Offset: 8 # CFGUnawareSrcSafetyAnalysis: src-state // CHECK-LABEL:Analyzing in function main, AllocatorId 1 .globl main