brintos

brintos / llvm-project-archived public Read only

0
0
Text · 68.2 KiB · 01b350b Raw
1821 lines · cpp
1//===- bolt/Passes/PAuthGadgetScanner.cpp ---------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a pass that looks for any AArch64 return instructions10// that may not be protected by PAuth authentication instructions when needed.11//12//===----------------------------------------------------------------------===//13 14#include "bolt/Passes/PAuthGadgetScanner.h"15#include "bolt/Core/ParallelUtilities.h"16#include "bolt/Passes/DataflowAnalysis.h"17#include "bolt/Utils/CommandLineOpts.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/ADT/SmallSet.h"20#include "llvm/MC/MCInst.h"21#include "llvm/Support/Format.h"22#include <memory>23 24#define DEBUG_TYPE "bolt-pauth-scanner"25 26namespace llvm {27namespace bolt {28namespace PAuthGadgetScanner {29 30static cl::opt<bool> AuthTrapsOnFailure(31    "auth-traps-on-failure",32    cl::desc("Assume authentication instructions always trap on failure"),33    cl::cat(opts::BinaryAnalysisCategory));34 35[[maybe_unused]] static void traceInst(const BinaryContext &BC, StringRef Label,36                                       const MCInst &MI) {37  dbgs() << "  " << Label << ": ";38  BC.printInstruction(dbgs(), MI);39}40 41[[maybe_unused]] static void traceReg(const BinaryContext &BC, StringRef Label,42                                      MCPhysReg Reg) {43  dbgs() << "    " << Label << ": ";44  if (Reg == BC.MIB->getNoRegister())45    dbgs() << "(none)";46  else47    dbgs() << BC.MRI->getName(Reg);48  dbgs() << "\n";49}50 51[[maybe_unused]] static void traceRegMask(const BinaryContext &BC,52                                          StringRef Label, BitVector Mask) {53  dbgs() << "    " << Label << ": ";54  RegStatePrinter(BC).print(dbgs(), Mask);55  dbgs() << "\n";56}57 58// Iterates over BinaryFunction's instructions like a range-based for loop:59//60// iterateOverInstrs(BF, [&](MCInstReference Inst) {61//   // loop body62// });63template <typename T> static void iterateOverInstrs(BinaryFunction &BF, T Fn) {64  if (BF.hasCFG()) {65    for (BinaryBasicBlock &BB : BF)66      for (int64_t I = 0, E = BB.size(); I < E; ++I)67        Fn(MCInstReference(BB, I));68  } else {69    for (auto I = BF.instrs().begin(), E = BF.instrs().end(); I != E; ++I)70      Fn(MCInstReference(BF, I));71  }72}73 74// This class represents mapping from a set of arbitrary physical registers to75// consecutive array indexes.76class TrackedRegisters {77  static constexpr uint16_t NoIndex = -1;78  const std::vector<MCPhysReg> Registers;79  std::vector<uint16_t> RegToIndexMapping;80 81  static size_t getMappingSize(ArrayRef<MCPhysReg> RegsToTrack) {82    if (RegsToTrack.empty())83      return 0;84    return 1 + *llvm::max_element(RegsToTrack);85  }86 87public:88  TrackedRegisters(ArrayRef<MCPhysReg> RegsToTrack)89      : Registers(RegsToTrack),90        RegToIndexMapping(getMappingSize(RegsToTrack), NoIndex) {91    for (auto [MappedIndex, Reg] : llvm::enumerate(RegsToTrack))92      RegToIndexMapping[Reg] = MappedIndex;93  }94 95  ArrayRef<MCPhysReg> getRegisters() const { return Registers; }96 97  size_t getNumTrackedRegisters() const { return Registers.size(); }98 99  bool empty() const { return Registers.empty(); }100 101  bool isTracked(MCPhysReg Reg) const {102    bool IsTracked = (unsigned)Reg < RegToIndexMapping.size() &&103                     RegToIndexMapping[Reg] != NoIndex;104    assert(IsTracked == llvm::is_contained(Registers, Reg));105    return IsTracked;106  }107 108  unsigned getIndex(MCPhysReg Reg) const {109    assert(isTracked(Reg) && "Register is not tracked");110    return RegToIndexMapping[Reg];111  }112};113 114// The security property that is checked is:115// When a register is used as the address to jump to in a return instruction,116// that register must be safe-to-dereference. It must either117// (a) be safe-to-dereference at function entry and never be changed within this118//     function, i.e. have the same value as when the function started, or119// (b) the last write to the register must be by an authentication instruction.120 121// This property is checked by using dataflow analysis to keep track of which122// registers have been written (def-ed), since last authenticated. For pac-ret,123// any return instruction using a register which is not safe-to-dereference is124// a gadget to be reported. For PAuthABI, probably at least any indirect control125// flow using such a register should be reported.126 127// Furthermore, when producing a diagnostic for a found non-pac-ret protected128// return, the analysis also lists the last instructions that wrote to the129// register used in the return instruction.130// The total set of registers used in return instructions in a given function is131// small. It almost always is just `X30`.132// In order to reduce the memory consumption of storing this additional state133// during the dataflow analysis, this is computed by running the dataflow134// analysis twice:135// 1. In the first run, the dataflow analysis only keeps track of the security136//    property: i.e. which registers have been overwritten since the last137//    time they've been authenticated.138// 2. If the first run finds any return instructions using a register last139//    written by a non-authenticating instruction, the dataflow analysis will140//    be run a second time. The first run will return which registers are used141//    in the gadgets to be reported. This information is used in the second run142//    to also track which instructions last wrote to those registers.143 144typedef SmallPtrSet<const MCInst *, 4> SetOfRelatedInsts;145 146/// A state representing which registers are safe to use by an instruction147/// at a given program point.148///149/// To simplify reasoning, let's stick with the following approach:150/// * when state is updated by the data-flow analysis, the sub-, super- and151///   overlapping registers are marked as needed152/// * when the particular instruction is checked if it represents a gadget,153///   the specific bit of BitVector should be usable to answer this.154///155/// For example, on AArch64:156/// * An AUTIZA X0 instruction marks both X0 and W0 (as well as W0_HI) as157///   safe-to-dereference. It does not change the state of X0_X1, for example,158///   as super-registers partially retain their old, unsafe values.159/// * LDR X1, [X0] marks as unsafe both X1 itself and anything it overlaps160///   with: W1, W1_HI, X0_X1 and so on.161/// * RET (which is implicitly RET X30) is a protected return if and only if162///   X30 is safe-to-dereference - the state computed for sub- and163///   super-registers is not inspected.164struct SrcState {165  /// A BitVector containing the registers that are either authenticated166  /// (assuming failed authentication is permitted to produce an invalid167  /// address, provided it generates an error on memory access) or whose168  /// value is known not to be attacker-controlled under Pointer Authentication169  /// threat model. The registers in this set are either170  /// * not clobbered since being authenticated, or171  /// * trusted at function entry and were not clobbered yet, or172  /// * contain a safely materialized address.173  BitVector SafeToDerefRegs;174  /// A BitVector containing the registers that are either authenticated175  /// *successfully* or whose value is known not to be attacker-controlled176  /// under Pointer Authentication threat model.177  /// The registers in this set are either178  /// * authenticated and then checked to be authenticated successfully179  ///   (and not clobbered since then), or180  /// * trusted at function entry and were not clobbered yet, or181  /// * contain a safely materialized address.182  BitVector TrustedRegs;183  /// A vector of sets, only used in the second data flow run.184  /// Each element in the vector represents one of the registers for which we185  /// track the set of last instructions that wrote to this register. For186  /// pac-ret analysis, the expectation is that almost all return instructions187  /// only use register `X30`, and therefore, this vector will probably have188  /// length 1 in the second run.189  std::vector<SetOfRelatedInsts> LastInstWritingReg;190 191  /// Construct an empty state.192  SrcState() {}193 194  SrcState(unsigned NumRegs, unsigned NumRegsToTrack)195      : SafeToDerefRegs(NumRegs), TrustedRegs(NumRegs),196        LastInstWritingReg(NumRegsToTrack) {}197 198  SrcState &merge(const SrcState &StateIn) {199    if (StateIn.empty())200      return *this;201    if (empty())202      return (*this = StateIn);203 204    SafeToDerefRegs &= StateIn.SafeToDerefRegs;205    TrustedRegs &= StateIn.TrustedRegs;206    for (auto [ThisSet, OtherSet] :207         llvm::zip_equal(LastInstWritingReg, StateIn.LastInstWritingReg))208      ThisSet.insert_range(OtherSet);209    return *this;210  }211 212  /// Returns true if this object does not store state of any registers -213  /// neither safe, nor unsafe ones.214  bool empty() const { return SafeToDerefRegs.empty(); }215 216  bool operator==(const SrcState &RHS) const {217    return SafeToDerefRegs == RHS.SafeToDerefRegs &&218           TrustedRegs == RHS.TrustedRegs &&219           LastInstWritingReg == RHS.LastInstWritingReg;220  }221  bool operator!=(const SrcState &RHS) const { return !((*this) == RHS); }222};223 224static void printInstsShort(raw_ostream &OS,225                            ArrayRef<SetOfRelatedInsts> Insts) {226  OS << "Insts: ";227  for (auto [I, PtrSet] : llvm::enumerate(Insts)) {228    OS << "[" << I << "](";229    interleave(PtrSet, OS, " ");230    OS << ")";231  }232}233 234static raw_ostream &operator<<(raw_ostream &OS, const SrcState &S) {235  OS << "src-state<";236  if (S.empty()) {237    OS << "empty";238  } else {239    OS << "SafeToDerefRegs: " << S.SafeToDerefRegs << ", ";240    OS << "TrustedRegs: " << S.TrustedRegs << ", ";241    printInstsShort(OS, S.LastInstWritingReg);242  }243  OS << ">";244  return OS;245}246 247class SrcStatePrinter {248public:249  void print(raw_ostream &OS, const SrcState &State) const;250  explicit SrcStatePrinter(const BinaryContext &BC) : BC(BC) {}251 252private:253  const BinaryContext &BC;254};255 256void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const {257  RegStatePrinter RegStatePrinter(BC);258  OS << "src-state<";259  if (S.empty()) {260    assert(S.SafeToDerefRegs.empty());261    assert(S.TrustedRegs.empty());262    assert(S.LastInstWritingReg.empty());263    OS << "empty";264  } else {265    OS << "SafeToDerefRegs: ";266    RegStatePrinter.print(OS, S.SafeToDerefRegs);267    OS << ", TrustedRegs: ";268    RegStatePrinter.print(OS, S.TrustedRegs);269    OS << ", ";270    printInstsShort(OS, S.LastInstWritingReg);271  }272  OS << ">";273}274 275/// Computes which registers are safe to be used by control flow and signing276/// instructions.277///278/// This is the base class for two implementations: a dataflow-based analysis279/// which is intended to be used for most functions and a simplified CFG-unaware280/// version for functions without reconstructed CFG.281class SrcSafetyAnalysis {282public:283  SrcSafetyAnalysis(BinaryFunction &BF, ArrayRef<MCPhysReg> RegsToTrackInstsFor)284      : BC(BF.getBinaryContext()), NumRegs(BC.MRI->getNumRegs()),285        RegsToTrackInstsFor(RegsToTrackInstsFor) {}286 287  virtual ~SrcSafetyAnalysis() {}288 289  static std::shared_ptr<SrcSafetyAnalysis>290  create(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,291         ArrayRef<MCPhysReg> RegsToTrackInstsFor);292 293  virtual void run() = 0;294  virtual const SrcState &getStateBefore(const MCInst &Inst) const = 0;295 296protected:297  BinaryContext &BC;298  const unsigned NumRegs;299  /// RegToTrackInstsFor is the set of registers for which the dataflow analysis300  /// must compute which the last set of instructions writing to it are.301  const TrackedRegisters RegsToTrackInstsFor;302  /// Stores information about the detected instruction sequences emitted to303  /// check an authenticated pointer. Specifically, if such sequence is detected304  /// in a basic block, it maps the last instruction of that basic block to305  /// (CheckedRegister, FirstInstOfTheSequence) pair, see the description of306  /// MCPlusBuilder::getAuthCheckedReg(BB) method.307  ///308  /// As the detection of such sequences requires iterating over the adjacent309  /// instructions, it should be done before calling computeNext(), which310  /// operates on separate instructions.311  DenseMap<const MCInst *, std::pair<MCPhysReg, const MCInst *>>312      CheckerSequenceInfo;313 314  SetOfRelatedInsts &lastWritingInsts(SrcState &S, MCPhysReg Reg) const {315    unsigned Index = RegsToTrackInstsFor.getIndex(Reg);316    return S.LastInstWritingReg[Index];317  }318  const SetOfRelatedInsts &lastWritingInsts(const SrcState &S,319                                            MCPhysReg Reg) const {320    unsigned Index = RegsToTrackInstsFor.getIndex(Reg);321    return S.LastInstWritingReg[Index];322  }323 324  SrcState createEntryState() {325    SrcState S(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());326    for (MCPhysReg Reg : BC.MIB->getTrustedLiveInRegs())327      S.TrustedRegs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);328    S.SafeToDerefRegs = S.TrustedRegs;329    return S;330  }331 332  /// Computes a reasonably pessimistic estimation of the register state when333  /// the previous instruction is not known for sure. Takes the set of registers334  /// which are trusted at function entry and removes all registers that can be335  /// clobbered inside this function.336  SrcState computePessimisticState(BinaryFunction &BF) {337    BitVector ClobberedRegs(NumRegs);338    iterateOverInstrs(BF, [&](MCInstReference Inst) {339      BC.MIB->getClobberedRegs(Inst, ClobberedRegs);340 341      // If this is a call instruction, no register is safe anymore, unless342      // it is a tail call. Ignore tail calls for the purpose of estimating the343      // worst-case scenario, assuming no instructions are executed in the344      // caller after this point anyway.345      if (BC.MIB->isCall(Inst) && !BC.MIB->isTailCall(Inst))346        ClobberedRegs.set();347    });348 349    SrcState S = createEntryState();350    S.SafeToDerefRegs.reset(ClobberedRegs);351    S.TrustedRegs.reset(ClobberedRegs);352    return S;353  }354 355  BitVector getClobberedRegs(const MCInst &Point) const {356    BitVector Clobbered(NumRegs);357    // Assume a call can clobber all registers, including callee-saved358    // registers. There's a good chance that callee-saved registers will be359    // saved on the stack at some point during execution of the callee.360    // Therefore they should also be considered as potentially modified by an361    // attacker/written to.362    // Also, not all functions may respect the AAPCS ABI rules about363    // caller/callee-saved registers.364    if (BC.MIB->isCall(Point))365      Clobbered.set();366    else367      BC.MIB->getClobberedRegs(Point, Clobbered);368    return Clobbered;369  }370 371  std::optional<MCPhysReg> getRegMadeTrustedByChecking(const MCInst &Inst,372                                                       SrcState Cur) const {373    // This function cannot return multiple registers. This is never the case374    // on AArch64.375    std::optional<MCPhysReg> RegCheckedByInst =376        BC.MIB->getAuthCheckedReg(Inst, /*MayOverwrite=*/false);377    if (RegCheckedByInst && Cur.SafeToDerefRegs[*RegCheckedByInst])378      return *RegCheckedByInst;379 380    auto It = CheckerSequenceInfo.find(&Inst);381    if (It == CheckerSequenceInfo.end())382      return std::nullopt;383 384    MCPhysReg RegCheckedBySequence = It->second.first;385    const MCInst *FirstCheckerInst = It->second.second;386 387    // FirstCheckerInst should belong to the same basic block (see the388    // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was389    // deterministically processed a few steps before this instruction.390    const SrcState &StateBeforeChecker = getStateBefore(*FirstCheckerInst);391 392    // The sequence checks the register, but it should be authenticated before.393    if (!StateBeforeChecker.SafeToDerefRegs[RegCheckedBySequence])394      return std::nullopt;395 396    return RegCheckedBySequence;397  }398 399  // Returns all registers that can be treated as if they are written by an400  // authentication instruction.401  SmallVector<MCPhysReg> getRegsMadeSafeToDeref(const MCInst &Point,402                                                const SrcState &Cur) const {403    SmallVector<MCPhysReg> Regs;404 405    // A signed pointer can be authenticated, or406    bool Dummy = false;407    if (auto AutReg = BC.MIB->getWrittenAuthenticatedReg(Point, Dummy))408      Regs.push_back(*AutReg);409 410    // ... a safe address can be materialized, or411    if (auto NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point))412      Regs.push_back(*NewAddrReg);413 414    // ... an address can be updated in a safe manner, producing the result415    // which is as trusted as the input address.416    if (auto DstAndSrc = BC.MIB->analyzeAddressArithmeticsForPtrAuth(Point)) {417      auto [DstReg, SrcReg] = *DstAndSrc;418      if (Cur.SafeToDerefRegs[SrcReg])419        Regs.push_back(DstReg);420    }421 422    // Make sure explicit checker sequence keeps register safe-to-dereference423    // when the register would be clobbered according to the regular rules:424    //425    //    ; LR is safe to dereference here426    //    mov   x16, x30  ; start of the sequence, LR is s-t-d right before427    //    xpaclri         ; clobbers LR, LR is not safe anymore428    //    cmp   x30, x16429    //    b.eq  1f        ; end of the sequence: LR is marked as trusted430    //    brk   0x1234431    //  1:432    //    ; at this point LR would be marked as trusted,433    //    ; but not safe-to-dereference434    //435    // or even just436    //437    //    ; X1 is safe to dereference here438    //    ldr x0, [x1, #8]!439    //    ; X1 is trusted here, but it was clobbered due to address write-back440    if (auto CheckedReg = getRegMadeTrustedByChecking(Point, Cur))441      Regs.push_back(*CheckedReg);442 443    return Regs;444  }445 446  // Returns all registers made trusted by this instruction.447  SmallVector<MCPhysReg> getRegsMadeTrusted(const MCInst &Point,448                                            const SrcState &Cur) const {449    assert(!AuthTrapsOnFailure && "Use getRegsMadeSafeToDeref instead");450    SmallVector<MCPhysReg> Regs;451 452    // An authenticated pointer can be checked, or453    if (auto CheckedReg = getRegMadeTrustedByChecking(Point, Cur))454      Regs.push_back(*CheckedReg);455 456    // ... a pointer can be authenticated by an instruction that always checks457    // the pointer, or458    bool IsChecked = false;459    std::optional<MCPhysReg> AutReg =460        BC.MIB->getWrittenAuthenticatedReg(Point, IsChecked);461    if (AutReg && IsChecked)462      Regs.push_back(*AutReg);463 464    // ... a safe address can be materialized, or465    if (auto NewAddrReg = BC.MIB->getMaterializedAddressRegForPtrAuth(Point))466      Regs.push_back(*NewAddrReg);467 468    // ... an address can be updated in a safe manner, producing the result469    // which is as trusted as the input address.470    if (auto DstAndSrc = BC.MIB->analyzeAddressArithmeticsForPtrAuth(Point)) {471      auto [DstReg, SrcReg] = *DstAndSrc;472      if (Cur.TrustedRegs[SrcReg])473        Regs.push_back(DstReg);474    }475 476    return Regs;477  }478 479  SrcState computeNext(const MCInst &Point, const SrcState &Cur) {480    if (BC.MIB->isCFI(Point))481      return Cur;482 483    SrcStatePrinter P(BC);484    LLVM_DEBUG({485      dbgs() << "  SrcSafetyAnalysis::ComputeNext(";486      BC.InstPrinter->printInst(&Point, 0, "", *BC.STI, dbgs());487      dbgs() << ", ";488      P.print(dbgs(), Cur);489      dbgs() << ")\n";490    });491 492    // If this instruction is reachable, a non-empty state will be propagated493    // to it from the entry basic block sooner or later. Until then, it is both494    // more efficient and easier to reason about to skip computeNext().495    if (Cur.empty()) {496      LLVM_DEBUG(497          { dbgs() << "Skipping computeNext(Point, Cur) as Cur is empty.\n"; });498      return SrcState();499    }500 501    // First, compute various properties of the instruction, taking the state502    // before its execution into account, if necessary.503 504    BitVector Clobbered = getClobberedRegs(Point);505    SmallVector<MCPhysReg> NewSafeToDerefRegs =506        getRegsMadeSafeToDeref(Point, Cur);507    // If authentication instructions trap on failure, safe-to-dereference508    // registers are always trusted.509    SmallVector<MCPhysReg> NewTrustedRegs =510        AuthTrapsOnFailure ? NewSafeToDerefRegs511                           : getRegsMadeTrusted(Point, Cur);512 513    // Then, compute the state after this instruction is executed.514    SrcState Next = Cur;515 516    Next.SafeToDerefRegs.reset(Clobbered);517    Next.TrustedRegs.reset(Clobbered);518    // Keep track of this instruction if it writes to any of the registers we519    // need to track that for:520    for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters())521      if (Clobbered[Reg])522        lastWritingInsts(Next, Reg) = {&Point};523 524    // After accounting for clobbered registers in general, override the state525    // according to authentication and other *special cases* of clobbering.526 527    // The sub-registers are also safe-to-dereference now, but not their528    // super-registers (as they retain untrusted register units).529    BitVector NewSafeSubregs(NumRegs);530    for (MCPhysReg SafeReg : NewSafeToDerefRegs)531      NewSafeSubregs |= BC.MIB->getAliases(SafeReg, /*OnlySmaller=*/true);532    for (MCPhysReg Reg : NewSafeSubregs.set_bits()) {533      Next.SafeToDerefRegs.set(Reg);534      if (RegsToTrackInstsFor.isTracked(Reg))535        lastWritingInsts(Next, Reg).clear();536    }537 538    // Process new trusted registers.539    for (MCPhysReg TrustedReg : NewTrustedRegs)540      Next.TrustedRegs |= BC.MIB->getAliases(TrustedReg, /*OnlySmaller=*/true);541 542    LLVM_DEBUG({543      dbgs() << "    .. result: (";544      P.print(dbgs(), Next);545      dbgs() << ")\n";546    });547 548    // Being trusted is a strictly stronger property than being549    // safe-to-dereference.550    assert(!Next.TrustedRegs.test(Next.SafeToDerefRegs) &&551           "SafeToDerefRegs should contain all TrustedRegs");552 553    return Next;554  }555 556public:557  std::vector<MCInstReference>558  getLastClobberingInsts(const MCInst &Inst, BinaryFunction &BF,559                         MCPhysReg ClobberedReg) const {560    const SrcState &S = getStateBefore(Inst);561 562    std::vector<MCInstReference> Result;563    for (const MCInst *Inst : lastWritingInsts(S, ClobberedReg))564      Result.push_back(MCInstReference::get(*Inst, BF));565    return Result;566  }567};568 569class DataflowSrcSafetyAnalysis570    : public SrcSafetyAnalysis,571      public DataflowAnalysis<DataflowSrcSafetyAnalysis, SrcState,572                              /*Backward=*/false, SrcStatePrinter> {573  using DFParent = DataflowAnalysis<DataflowSrcSafetyAnalysis, SrcState, false,574                                    SrcStatePrinter>;575  friend DFParent;576 577  using SrcSafetyAnalysis::BC;578  using SrcSafetyAnalysis::computeNext;579 580  // Pessimistic initial state for basic blocks without any predecessors581  // (not needed for most functions, thus initialized lazily).582  SrcState PessimisticState;583 584public:585  DataflowSrcSafetyAnalysis(BinaryFunction &BF,586                            MCPlusBuilder::AllocatorIdTy AllocId,587                            ArrayRef<MCPhysReg> RegsToTrackInstsFor)588      : SrcSafetyAnalysis(BF, RegsToTrackInstsFor), DFParent(BF, AllocId) {}589 590  const SrcState &getStateBefore(const MCInst &Inst) const override {591    return DFParent::getStateBefore(Inst).get();592  }593 594  void run() override {595    for (BinaryBasicBlock &BB : Func) {596      if (auto CheckerInfo = BC.MIB->getAuthCheckedReg(BB)) {597        MCPhysReg CheckedReg = CheckerInfo->first;598        MCInst &FirstInst = *CheckerInfo->second;599        MCInst &LastInst = *BB.getLastNonPseudoInstr();600        LLVM_DEBUG({601          dbgs() << "Found pointer checking sequence in " << BB.getName()602                 << ":\n";603          traceReg(BC, "Checked register", CheckedReg);604          traceInst(BC, "First instruction", FirstInst);605          traceInst(BC, "Last instruction", LastInst);606        });607        (void)CheckedReg;608        (void)FirstInst;609        assert(llvm::any_of(BB, [&](MCInst &I) { return &I == &FirstInst; }) &&610               "Data-flow analysis expects the checker not to cross BBs");611        CheckerSequenceInfo[&LastInst] = *CheckerInfo;612      }613    }614    DFParent::run();615  }616 617protected:618  void preflight() {}619 620  SrcState getStartingStateAtBB(const BinaryBasicBlock &BB) {621    if (BB.isEntryPoint())622      return createEntryState();623 624    // If a basic block without any predecessors is found in an optimized code,625    // this likely means that some CFG edges were not detected. Pessimistically626    // assume any register that can ever be clobbered in this function to be627    // unsafe before this basic block.628    // Warn about this fact in FunctionAnalysis::findUnsafeUses(), as it likely629    // means imprecise CFG information.630    if (BB.pred_empty()) {631      if (PessimisticState.empty())632        PessimisticState = computePessimisticState(*BB.getParent());633      return PessimisticState;634    }635 636    return SrcState();637  }638 639  SrcState getStartingStateAtPoint(const MCInst &Point) { return SrcState(); }640 641  void doConfluence(SrcState &StateOut, const SrcState &StateIn) {642    SrcStatePrinter P(BC);643    LLVM_DEBUG({644      dbgs() << "  DataflowSrcSafetyAnalysis::Confluence(\n";645      dbgs() << "    State 1: ";646      P.print(dbgs(), StateOut);647      dbgs() << "\n";648      dbgs() << "    State 2: ";649      P.print(dbgs(), StateIn);650      dbgs() << ")\n";651    });652 653    StateOut.merge(StateIn);654 655    LLVM_DEBUG({656      dbgs() << "    merged state: ";657      P.print(dbgs(), StateOut);658      dbgs() << "\n";659    });660  }661 662  StringRef getAnnotationName() const { return "DataflowSrcSafetyAnalysis"; }663};664 665/// A helper base class for implementing a simplified counterpart of a dataflow666/// analysis for functions without CFG information.667template <typename StateTy> class CFGUnawareAnalysis {668  BinaryContext &BC;669  BinaryFunction &BF;670  MCPlusBuilder::AllocatorIdTy AllocId;671  unsigned StateAnnotationIndex;672 673  void cleanStateAnnotations() {674    for (auto &I : BF.instrs())675      BC.MIB->removeAnnotation(I.second, StateAnnotationIndex);676  }677 678protected:679  CFGUnawareAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,680                     StringRef AnnotationName)681      : BC(BF.getBinaryContext()), BF(BF), AllocId(AllocId) {682    StateAnnotationIndex = BC.MIB->getOrCreateAnnotationIndex(AnnotationName);683  }684 685  void setState(MCInst &Inst, const StateTy &S) {686    // Check if we need to remove an old annotation (this is the case if687    // this is the second, detailed run of the analysis).688    if (BC.MIB->hasAnnotation(Inst, StateAnnotationIndex))689      BC.MIB->removeAnnotation(Inst, StateAnnotationIndex);690    // Attach the state.691    BC.MIB->addAnnotation(Inst, StateAnnotationIndex, S, AllocId);692  }693 694  const StateTy &getState(const MCInst &Inst) const {695    return BC.MIB->getAnnotationAs<StateTy>(Inst, StateAnnotationIndex);696  }697 698  virtual ~CFGUnawareAnalysis() { cleanStateAnnotations(); }699};700 701// A simplified implementation of DataflowSrcSafetyAnalysis for functions702// lacking CFG information.703//704// Let assume the instructions can only be executed linearly unless there is705// a label to jump to - this should handle both directly jumping to a location706// encoded as an immediate operand of a branch instruction, as well as saving a707// branch destination somewhere and passing it to an indirect branch instruction708// later, provided no arithmetic is performed on the destination address:709//710//     ; good: the destination is directly encoded into the branch instruction711//     cbz x0, some_label712//713//     ; good: the branch destination is first stored and then used as-is714//     adr x1, some_label715//     br  x1716//717//     ; bad: some clever arithmetic is performed manually718//     adr x1, some_label719//     add x1, x1, #4720//     br  x1721//     ...722//   some_label:723//     ; pessimistically reset the state as we are unsure where we came from724//     ...725//     ret726//   JTI0:727//     .byte some_label - Ltmp0 ; computing offsets using labels may probably728//                                work too, provided enough information is729//                                retained by the assembler and linker730//731// Then, a function can be split into a number of disjoint contiguous sequences732// of instructions without labels in between. These sequences can be processed733// the same way basic blocks are processed by data-flow analysis, with the same734// pessimistic estimation of the initial state at the start of each sequence735// (except the first instruction of the function).736class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis,737                                    public CFGUnawareAnalysis<SrcState> {738  using SrcSafetyAnalysis::BC;739  BinaryFunction &BF;740 741public:742  CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF,743                              MCPlusBuilder::AllocatorIdTy AllocId,744                              ArrayRef<MCPhysReg> RegsToTrackInstsFor)745      : SrcSafetyAnalysis(BF, RegsToTrackInstsFor),746        CFGUnawareAnalysis(BF, AllocId, "CFGUnawareSrcSafetyAnalysis"), BF(BF) {747  }748 749  void run() override {750    const SrcState DefaultState = computePessimisticState(BF);751    SrcState S = createEntryState();752    for (auto &I : BF.instrs()) {753      MCInst &Inst = I.second;754      if (BC.MIB->isCFI(Inst))755        continue;756 757      // If there is a label before this instruction, it is possible that it758      // can be jumped-to, thus conservatively resetting S. As an exception,759      // let's ignore any labels at the beginning of the function, as at least760      // one label is expected there.761      if (BF.hasLabelAt(I.first) && &Inst != &BF.instrs().begin()->second) {762        LLVM_DEBUG({763          traceInst(BC, "Due to label, resetting the state before", Inst);764        });765        S = DefaultState;766      }767 768      // Attach the state *before* this instruction executes.769      setState(Inst, S);770 771      // Compute the state after this instruction executes.772      S = computeNext(Inst, S);773    }774  }775 776  const SrcState &getStateBefore(const MCInst &Inst) const override {777    return getState(Inst);778  }779};780 781std::shared_ptr<SrcSafetyAnalysis>782SrcSafetyAnalysis::create(BinaryFunction &BF,783                          MCPlusBuilder::AllocatorIdTy AllocId,784                          ArrayRef<MCPhysReg> RegsToTrackInstsFor) {785  if (BF.hasCFG())786    return std::make_shared<DataflowSrcSafetyAnalysis>(BF, AllocId,787                                                       RegsToTrackInstsFor);788  return std::make_shared<CFGUnawareSrcSafetyAnalysis>(BF, AllocId,789                                                       RegsToTrackInstsFor);790}791 792/// A state representing which registers are safe to be used as the destination793/// operand of an authentication instruction.794///795/// Similar to SrcState, it is the responsibility of the analysis to take796/// register aliasing into account.797///798/// Depending on the implementation (such as whether FEAT_FPAC is implemented799/// by an AArch64 CPU or not), it may be possible that an authentication800/// instruction returns an invalid pointer on failure instead of terminating801/// the program immediately (assuming the program will crash as soon as that802/// pointer is dereferenced). Since few bits are usually allocated for the PAC803/// field (such as less than 16 bits on a typical AArch64 system), an attacker804/// can try every possible signature and guess the correct one if there is a805/// gadget that tells whether the particular pointer has a correct signature806/// (a so called "authentication oracle"). For that reason, it should be807/// impossible for an attacker to test if a pointer is correctly signed -808/// either the program should be terminated on authentication failure or809/// the result of authentication should not be accessible to an attacker.810///811/// Considering the instructions in forward order as they are executed, a812/// restricted set of operations can be allowed on any register containing a813/// value derived from the result of an authentication instruction until that814/// value is checked not to contain the result of a failed authentication.815/// In DstSafetyAnalysis, these rules are adapted, so that the safety property816/// for a register is computed by iterating the instructions in backward order.817/// Then the resulting properties are used at authentication instruction sites818/// to check output registers and report the particular instruction if it writes819/// to an unsafe register.820///821/// Another approach would be to simulate the above rules as-is, iterating over822/// the instructions in forward direction. To make it possible to report the823/// particular instructions as oracles, this would probably require tracking824/// references to these instructions for each register currently containing825/// sensitive data.826///827/// In DstSafetyAnalysis, the source register Xn of an instruction Inst is safe828/// if at least one of the following is true:829/// * Inst checks if Xn contains the result of a successful authentication and830///   terminates the program on failure. Note that Inst can either naturally831///   dereference Xn (load, branch, return, etc. instructions) or be the first832///   instruction of an explicit checking sequence.833/// * Inst performs safe address arithmetic AND both source and result834///   registers, as well as any temporary registers, must be safe after835///   execution of Inst (temporaries are not used on AArch64 and thus not836///   currently supported/allowed).837///   See MCPlusBuilder::analyzeAddressArithmeticsForPtrAuth for the details.838/// * Inst fully overwrites Xn with a constant.839struct DstState {840  /// The set of registers whose values cannot be inspected by an attacker in841  /// a way usable as an authentication oracle. The results of authentication842  /// instructions should only be written to such registers.843  BitVector CannotEscapeUnchecked;844 845  /// A vector of sets, only used on the second analysis run.846  /// Each element in this vector represents one of the tracked registers.847  /// For each such register we track the set of first instructions that leak848  /// the authenticated pointer before it was checked. This is intended to849  /// provide clues on which instruction made the particular register unsafe.850  ///851  /// Please note that the mapping from MCPhysReg values to indexes in this852  /// vector is provided by RegsToTrackInstsFor field of DstSafetyAnalysis.853  std::vector<SetOfRelatedInsts> FirstInstLeakingReg;854 855  /// Constructs an empty state.856  DstState() {}857 858  DstState(unsigned NumRegs, unsigned NumRegsToTrack)859      : CannotEscapeUnchecked(NumRegs), FirstInstLeakingReg(NumRegsToTrack) {}860 861  DstState &merge(const DstState &StateIn) {862    if (StateIn.empty())863      return *this;864    if (empty())865      return (*this = StateIn);866 867    CannotEscapeUnchecked &= StateIn.CannotEscapeUnchecked;868    for (auto [ThisSet, OtherSet] :869         llvm::zip_equal(FirstInstLeakingReg, StateIn.FirstInstLeakingReg))870      ThisSet.insert_range(OtherSet);871    return *this;872  }873 874  /// Returns true if this object does not store state of any registers -875  /// neither safe, nor unsafe ones.876  bool empty() const { return CannotEscapeUnchecked.empty(); }877 878  bool operator==(const DstState &RHS) const {879    return CannotEscapeUnchecked == RHS.CannotEscapeUnchecked &&880           FirstInstLeakingReg == RHS.FirstInstLeakingReg;881  }882  bool operator!=(const DstState &RHS) const { return !((*this) == RHS); }883};884 885static raw_ostream &operator<<(raw_ostream &OS, const DstState &S) {886  OS << "dst-state<";887  if (S.empty()) {888    OS << "empty";889  } else {890    OS << "CannotEscapeUnchecked: " << S.CannotEscapeUnchecked << ", ";891    printInstsShort(OS, S.FirstInstLeakingReg);892  }893  OS << ">";894  return OS;895}896 897class DstStatePrinter {898public:899  void print(raw_ostream &OS, const DstState &S) const;900  explicit DstStatePrinter(const BinaryContext &BC) : BC(BC) {}901 902private:903  const BinaryContext &BC;904};905 906void DstStatePrinter::print(raw_ostream &OS, const DstState &S) const {907  RegStatePrinter RegStatePrinter(BC);908  OS << "dst-state<";909  if (S.empty()) {910    assert(S.CannotEscapeUnchecked.empty());911    assert(S.FirstInstLeakingReg.empty());912    OS << "empty";913  } else {914    OS << "CannotEscapeUnchecked: ";915    RegStatePrinter.print(OS, S.CannotEscapeUnchecked);916    OS << ", ";917    printInstsShort(OS, S.FirstInstLeakingReg);918  }919  OS << ">";920}921 922/// Computes which registers are safe to be written to by auth instructions.923///924/// This is the base class for two implementations: a dataflow-based analysis925/// which is intended to be used for most functions and a simplified CFG-unaware926/// version for functions without reconstructed CFG.927class DstSafetyAnalysis {928public:929  DstSafetyAnalysis(BinaryFunction &BF, ArrayRef<MCPhysReg> RegsToTrackInstsFor)930      : BC(BF.getBinaryContext()), NumRegs(BC.MRI->getNumRegs()),931        RegsToTrackInstsFor(RegsToTrackInstsFor) {}932 933  virtual ~DstSafetyAnalysis() {}934 935  static std::shared_ptr<DstSafetyAnalysis>936  create(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,937         ArrayRef<MCPhysReg> RegsToTrackInstsFor);938 939  virtual void run() = 0;940  virtual const DstState &getStateAfter(const MCInst &Inst) const = 0;941 942protected:943  BinaryContext &BC;944  const unsigned NumRegs;945 946  const TrackedRegisters RegsToTrackInstsFor;947 948  /// Stores information about the detected instruction sequences emitted to949  /// check an authenticated pointer. Specifically, if such sequence is detected950  /// in a basic block, it maps the first instruction of that sequence to the951  /// register being checked.952  ///953  /// As the detection of such sequences requires iterating over the adjacent954  /// instructions, it should be done before calling computeNext(), which955  /// operates on separate instructions.956  DenseMap<const MCInst *, MCPhysReg> RegCheckedAt;957 958  SetOfRelatedInsts &firstLeakingInsts(DstState &S, MCPhysReg Reg) const {959    unsigned Index = RegsToTrackInstsFor.getIndex(Reg);960    return S.FirstInstLeakingReg[Index];961  }962  const SetOfRelatedInsts &firstLeakingInsts(const DstState &S,963                                             MCPhysReg Reg) const {964    unsigned Index = RegsToTrackInstsFor.getIndex(Reg);965    return S.FirstInstLeakingReg[Index];966  }967 968  /// Creates a state with all registers marked unsafe (not to be confused969  /// with empty state).970  DstState createUnsafeState() {971    return DstState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());972  }973 974  /// Returns the set of registers that can be leaked by this instruction.975  /// A register is considered leaked if it has any intersection with any976  /// register read by Inst. This is similar to how the set of clobbered977  /// registers is computed, but taking input operands instead of outputs.978  BitVector getLeakedRegs(const MCInst &Inst) const {979    BitVector Leaked(NumRegs);980 981    // Assume a call can read all registers.982    if (BC.MIB->isCall(Inst)) {983      Leaked.set();984      return Leaked;985    }986 987    // Compute the set of registers overlapping with any register used by988    // this instruction.989 990    const MCInstrDesc &Desc = BC.MII->get(Inst.getOpcode());991 992    for (MCPhysReg Reg : Desc.implicit_uses())993      Leaked |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/false);994 995    for (const MCOperand &Op : BC.MIB->useOperands(Inst)) {996      if (Op.isReg())997        Leaked |= BC.MIB->getAliases(Op.getReg(), /*OnlySmaller=*/false);998    }999 1000    return Leaked;1001  }1002 1003  SmallVector<MCPhysReg> getRegsMadeProtected(const MCInst &Inst,1004                                              const BitVector &LeakedRegs,1005                                              const DstState &Cur) const {1006    SmallVector<MCPhysReg> Regs;1007 1008    // A pointer can be checked, or1009    if (auto CheckedReg =1010            BC.MIB->getAuthCheckedReg(Inst, /*MayOverwrite=*/true))1011      Regs.push_back(*CheckedReg);1012    if (RegCheckedAt.contains(&Inst))1013      Regs.push_back(RegCheckedAt.at(&Inst));1014 1015    // ... it can be used as a branch target, or1016    if (BC.MIB->isIndirectBranch(Inst) || BC.MIB->isIndirectCall(Inst)) {1017      bool IsAuthenticated;1018      MCPhysReg BranchDestReg =1019          BC.MIB->getRegUsedAsIndirectBranchDest(Inst, IsAuthenticated);1020      assert(BranchDestReg != BC.MIB->getNoRegister());1021      if (!IsAuthenticated)1022        Regs.push_back(BranchDestReg);1023    }1024 1025    // ... it can be used as a return target, or1026    if (BC.MIB->isReturn(Inst)) {1027      bool IsAuthenticated = false;1028      std::optional<MCPhysReg> RetReg =1029          BC.MIB->getRegUsedAsRetDest(Inst, IsAuthenticated);1030      if (RetReg && !IsAuthenticated)1031        Regs.push_back(*RetReg);1032    }1033 1034    // ... an address can be updated in a safe manner, or1035    if (auto DstAndSrc = BC.MIB->analyzeAddressArithmeticsForPtrAuth(Inst)) {1036      auto [DstReg, SrcReg] = *DstAndSrc;1037      // Note that *all* registers containing the derived values must be safe,1038      // both source and destination ones. No temporaries are supported at now.1039      if (Cur.CannotEscapeUnchecked[SrcReg] &&1040          Cur.CannotEscapeUnchecked[DstReg])1041        Regs.push_back(SrcReg);1042    }1043 1044    // ... the register can be overwritten in whole with a constant: for that1045    // purpose, look for the instructions with no register inputs (neither1046    // explicit nor implicit ones) and no side effects (to rule out reading1047    // not modelled locations).1048    const MCInstrDesc &Desc = BC.MII->get(Inst.getOpcode());1049    bool HasExplicitSrcRegs = llvm::any_of(BC.MIB->useOperands(Inst),1050                                           [](auto Op) { return Op.isReg(); });1051    if (!Desc.hasUnmodeledSideEffects() && !HasExplicitSrcRegs &&1052        Desc.implicit_uses().empty()) {1053      for (const MCOperand &Def : BC.MIB->defOperands(Inst))1054        Regs.push_back(Def.getReg());1055    }1056 1057    return Regs;1058  }1059 1060  DstState computeNext(const MCInst &Point, const DstState &Cur) {1061    if (BC.MIB->isCFI(Point))1062      return Cur;1063 1064    DstStatePrinter P(BC);1065    LLVM_DEBUG({1066      dbgs() << "  DstSafetyAnalysis::ComputeNext(";1067      BC.InstPrinter->printInst(&Point, 0, "", *BC.STI, dbgs());1068      dbgs() << ", ";1069      P.print(dbgs(), Cur);1070      dbgs() << ")\n";1071    });1072 1073    // If this instruction terminates the program immediately, no1074    // authentication oracles are possible past this point.1075    if (BC.MIB->isTrap(Point)) {1076      LLVM_DEBUG(traceInst(BC, "Trap instruction found", Point));1077      DstState Next(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());1078      Next.CannotEscapeUnchecked.set();1079      return Next;1080    }1081 1082    // If this instruction is reachable by the analysis, a non-empty state will1083    // be propagated to it sooner or later. Until then, skip computeNext().1084    if (Cur.empty()) {1085      LLVM_DEBUG(1086          { dbgs() << "Skipping computeNext(Point, Cur) as Cur is empty.\n"; });1087      return DstState();1088    }1089 1090    // First, compute various properties of the instruction, taking the state1091    // after its execution into account, if necessary.1092 1093    BitVector LeakedRegs = getLeakedRegs(Point);1094    SmallVector<MCPhysReg> NewProtectedRegs =1095        getRegsMadeProtected(Point, LeakedRegs, Cur);1096 1097    // Then, compute the state before this instruction is executed.1098    DstState Next = Cur;1099 1100    Next.CannotEscapeUnchecked.reset(LeakedRegs);1101    for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters()) {1102      if (LeakedRegs[Reg])1103        firstLeakingInsts(Next, Reg) = {&Point};1104    }1105 1106    BitVector NewProtectedSubregs(NumRegs);1107    for (MCPhysReg Reg : NewProtectedRegs)1108      NewProtectedSubregs |= BC.MIB->getAliases(Reg, /*OnlySmaller=*/true);1109    Next.CannotEscapeUnchecked |= NewProtectedSubregs;1110    for (MCPhysReg Reg : RegsToTrackInstsFor.getRegisters()) {1111      if (NewProtectedSubregs[Reg])1112        firstLeakingInsts(Next, Reg).clear();1113    }1114 1115    LLVM_DEBUG({1116      dbgs() << "    .. result: (";1117      P.print(dbgs(), Next);1118      dbgs() << ")\n";1119    });1120 1121    return Next;1122  }1123 1124public:1125  std::vector<MCInstReference> getLeakingInsts(const MCInst &Inst,1126                                               BinaryFunction &BF,1127                                               MCPhysReg LeakedReg) const {1128    const DstState &S = getStateAfter(Inst);1129 1130    std::vector<MCInstReference> Result;1131    for (const MCInst *Inst : firstLeakingInsts(S, LeakedReg))1132      Result.push_back(MCInstReference::get(*Inst, BF));1133    return Result;1134  }1135};1136 1137class DataflowDstSafetyAnalysis1138    : public DstSafetyAnalysis,1139      public DataflowAnalysis<DataflowDstSafetyAnalysis, DstState,1140                              /*Backward=*/true, DstStatePrinter> {1141  using DFParent = DataflowAnalysis<DataflowDstSafetyAnalysis, DstState, true,1142                                    DstStatePrinter>;1143  friend DFParent;1144 1145  using DstSafetyAnalysis::BC;1146  using DstSafetyAnalysis::computeNext;1147 1148public:1149  DataflowDstSafetyAnalysis(BinaryFunction &BF,1150                            MCPlusBuilder::AllocatorIdTy AllocId,1151                            ArrayRef<MCPhysReg> RegsToTrackInstsFor)1152      : DstSafetyAnalysis(BF, RegsToTrackInstsFor), DFParent(BF, AllocId) {}1153 1154  const DstState &getStateAfter(const MCInst &Inst) const override {1155    // The dataflow analysis base class iterates backwards over the1156    // instructions, thus "after" vs. "before" difference.1157    return DFParent::getStateBefore(Inst).get();1158  }1159 1160  void run() override {1161    // As long as DstSafetyAnalysis is only computed to detect authentication1162    // oracles, it is a waste of time to compute it when authentication1163    // instructions are known to always trap on failure.1164    assert(!AuthTrapsOnFailure &&1165           "DstSafetyAnalysis is useless with faulting auth");1166    for (BinaryBasicBlock &BB : Func) {1167      if (auto CheckerInfo = BC.MIB->getAuthCheckedReg(BB)) {1168        LLVM_DEBUG({1169          dbgs() << "Found pointer checking sequence in " << BB.getName()1170                 << ":\n";1171          traceReg(BC, "Checked register", CheckerInfo->first);1172          traceInst(BC, "First instruction", *CheckerInfo->second);1173        });1174        RegCheckedAt[CheckerInfo->second] = CheckerInfo->first;1175      }1176    }1177    DFParent::run();1178  }1179 1180protected:1181  void preflight() {}1182 1183  DstState getStartingStateAtBB(const BinaryBasicBlock &BB) {1184    // In general, the initial state should be empty, not everything-is-unsafe,1185    // to give a chance for some meaningful state to be propagated to BB from1186    // an indirectly reachable "exit basic block" ending with a return or tail1187    // call instruction.1188    //1189    // A basic block without any successors, on the other hand, can be1190    // pessimistically initialized to everything-is-unsafe: this will naturally1191    // handle return, trap and tail call instructions. At the same time, it is1192    // harmless for internal indirect branch instructions, like computed gotos.1193    if (BB.succ_empty())1194      return createUnsafeState();1195 1196    return DstState();1197  }1198 1199  DstState getStartingStateAtPoint(const MCInst &Point) { return DstState(); }1200 1201  void doConfluence(DstState &StateOut, const DstState &StateIn) {1202    DstStatePrinter P(BC);1203    LLVM_DEBUG({1204      dbgs() << "  DataflowDstSafetyAnalysis::Confluence(\n";1205      dbgs() << "    State 1: ";1206      P.print(dbgs(), StateOut);1207      dbgs() << "\n";1208      dbgs() << "    State 2: ";1209      P.print(dbgs(), StateIn);1210      dbgs() << ")\n";1211    });1212 1213    StateOut.merge(StateIn);1214 1215    LLVM_DEBUG({1216      dbgs() << "    merged state: ";1217      P.print(dbgs(), StateOut);1218      dbgs() << "\n";1219    });1220  }1221 1222  StringRef getAnnotationName() const { return "DataflowDstSafetyAnalysis"; }1223};1224 1225class CFGUnawareDstSafetyAnalysis : public DstSafetyAnalysis,1226                                    public CFGUnawareAnalysis<DstState> {1227  using DstSafetyAnalysis::BC;1228  BinaryFunction &BF;1229 1230public:1231  CFGUnawareDstSafetyAnalysis(BinaryFunction &BF,1232                              MCPlusBuilder::AllocatorIdTy AllocId,1233                              ArrayRef<MCPhysReg> RegsToTrackInstsFor)1234      : DstSafetyAnalysis(BF, RegsToTrackInstsFor),1235        CFGUnawareAnalysis(BF, AllocId, "CFGUnawareDstSafetyAnalysis"), BF(BF) {1236  }1237 1238  void run() override {1239    DstState S = createUnsafeState();1240    for (auto &I : llvm::reverse(BF.instrs())) {1241      MCInst &Inst = I.second;1242      if (BC.MIB->isCFI(Inst))1243        continue;1244 1245      // If Inst can change the control flow, we cannot be sure that the next1246      // instruction (to be executed in analyzed program) is the one processed1247      // on the previous iteration, thus pessimistically reset S before1248      // starting to analyze Inst.1249      if (BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst) ||1250          BC.MIB->isReturn(Inst)) {1251        LLVM_DEBUG(traceInst(BC, "Control flow instruction", Inst));1252        S = createUnsafeState();1253      }1254 1255      // Attach the state *after* this instruction executes.1256      setState(Inst, S);1257 1258      // Compute the next state.1259      S = computeNext(Inst, S);1260    }1261  }1262 1263  const DstState &getStateAfter(const MCInst &Inst) const override {1264    return getState(Inst);1265  }1266};1267 1268std::shared_ptr<DstSafetyAnalysis>1269DstSafetyAnalysis::create(BinaryFunction &BF,1270                          MCPlusBuilder::AllocatorIdTy AllocId,1271                          ArrayRef<MCPhysReg> RegsToTrackInstsFor) {1272  if (BF.hasCFG())1273    return std::make_shared<DataflowDstSafetyAnalysis>(BF, AllocId,1274                                                       RegsToTrackInstsFor);1275  return std::make_shared<CFGUnawareDstSafetyAnalysis>(BF, AllocId,1276                                                       RegsToTrackInstsFor);1277}1278 1279// This function could return PartialReport<T>, but currently T is always1280// MCPhysReg, even though it is an implementation detail.1281static PartialReport<MCPhysReg> make_generic_report(MCInstReference Location,1282                                                    StringRef Text) {1283  auto Report = std::make_shared<GenericDiagnostic>(Location, Text);1284  return PartialReport<MCPhysReg>(Report, std::nullopt);1285}1286 1287template <typename T>1288static PartialReport<T> make_gadget_report(const GadgetKind &Kind,1289                                           MCInstReference Location,1290                                           T RequestedDetails) {1291  auto Report = std::make_shared<GadgetDiagnostic>(Kind, Location);1292  return PartialReport<T>(Report, RequestedDetails);1293}1294 1295static std::optional<PartialReport<MCPhysReg>>1296shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst,1297                         const SrcState &S) {1298  static const GadgetKind RetKind("non-protected ret found");1299  if (!BC.MIB->isReturn(Inst))1300    return std::nullopt;1301 1302  bool IsAuthenticated = false;1303  std::optional<MCPhysReg> RetReg =1304      BC.MIB->getRegUsedAsRetDest(Inst, IsAuthenticated);1305  if (!RetReg) {1306    return make_generic_report(1307        Inst, "Warning: pac-ret analysis could not analyze this return "1308              "instruction");1309  }1310  if (IsAuthenticated)1311    return std::nullopt;1312 1313  LLVM_DEBUG({1314    traceInst(BC, "Found RET inst", Inst);1315    traceReg(BC, "RetReg", *RetReg);1316    traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);1317  });1318 1319  if (S.SafeToDerefRegs[*RetReg])1320    return std::nullopt;1321 1322  return make_gadget_report(RetKind, Inst, *RetReg);1323}1324 1325/// While BOLT already marks some of the branch instructions as tail calls,1326/// this function tries to detect less obvious cases, assuming false positives1327/// are acceptable as long as there are not too many of them.1328///1329/// It is possible that not all the instructions classified as tail calls by1330/// this function are safe to be considered as such for the purpose of code1331/// transformations performed by BOLT. The intention of this function is to1332/// spot some of actually missed tail calls (and likely a number of unrelated1333/// indirect branch instructions) as long as this doesn't increase the amount1334/// of false positive reports unacceptably.1335static bool shouldAnalyzeTailCallInst(const BinaryContext &BC,1336                                      const BinaryFunction &BF,1337                                      const MCInstReference &Inst) {1338  // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ()1339  // (such as isBranch at the time of writing this comment), some don't (such1340  // as isCall). For that reason, call MCInstrDesc's methods explicitly when1341  // it is important.1342  const MCInstrDesc &Desc = BC.MII->get(Inst.getMCInst().getOpcode());1343  // Tail call should be a branch (but not necessarily an indirect one).1344  if (!Desc.isBranch())1345    return false;1346 1347  // Always analyze the branches already marked as tail calls by BOLT.1348  if (BC.MIB->isTailCall(Inst))1349    return true;1350 1351  // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the1352  // below is a simplified condition from BinaryContext::printInstruction.1353  bool IsUnknownControlFlow =1354      BC.MIB->isIndirectBranch(Inst) && !BC.MIB->getJumpTable(Inst);1355 1356  if (BF.hasCFG() && IsUnknownControlFlow)1357    return true;1358 1359  return false;1360}1361 1362static std::optional<PartialReport<MCPhysReg>>1363shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF,1364                           const MCInstReference &Inst, const SrcState &S) {1365  static const GadgetKind UntrustedLRKind(1366      "untrusted link register found before tail call");1367 1368  if (!shouldAnalyzeTailCallInst(BC, BF, Inst))1369    return std::nullopt;1370 1371  // Not only the set of registers returned by getTrustedLiveInRegs() can be1372  // seen as a reasonable target-independent _approximation_ of "the LR", these1373  // are *exactly* those registers used by SrcSafetyAnalysis to initialize the1374  // set of trusted registers on function entry.1375  // Thus, this function basically checks that the precondition expected to be1376  // imposed by a function call instruction (which is hardcoded into the target-1377  // specific getTrustedLiveInRegs() function) is also respected on tail calls.1378  SmallVector<MCPhysReg> RegsToCheck = BC.MIB->getTrustedLiveInRegs();1379  LLVM_DEBUG({1380    traceInst(BC, "Found tail call inst", Inst);1381    traceRegMask(BC, "Trusted regs", S.TrustedRegs);1382  });1383 1384  // In musl on AArch64, the _start function sets LR to zero and calls the next1385  // stage initialization function at the end, something along these lines:1386  //1387  //   _start:1388  //     mov     x30, #01389  //     ; ... other initialization ...1390  //     b       _start_c ; performs "exit" system call at some point1391  //1392  // As this would produce a false positive for every executable linked with1393  // such libc, ignore tail calls performed by ELF entry function.1394  if (BC.StartFunctionAddress &&1395      *BC.StartFunctionAddress == Inst.getFunction()->getAddress()) {1396    LLVM_DEBUG(dbgs() << "  Skipping tail call in ELF entry function.\n");1397    return std::nullopt;1398  }1399 1400  // Returns at most one report per instruction - this is probably OK...1401  for (auto Reg : RegsToCheck)1402    if (!S.TrustedRegs[Reg])1403      return make_gadget_report(UntrustedLRKind, Inst, Reg);1404 1405  return std::nullopt;1406}1407 1408static std::optional<PartialReport<MCPhysReg>>1409shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst,1410                       const SrcState &S) {1411  static const GadgetKind CallKind("non-protected call found");1412  if (!BC.MIB->isIndirectCall(Inst) && !BC.MIB->isIndirectBranch(Inst))1413    return std::nullopt;1414 1415  bool IsAuthenticated = false;1416  MCPhysReg DestReg =1417      BC.MIB->getRegUsedAsIndirectBranchDest(Inst, IsAuthenticated);1418  if (IsAuthenticated)1419    return std::nullopt;1420 1421  assert(DestReg != BC.MIB->getNoRegister() && "Valid register expected");1422  LLVM_DEBUG({1423    traceInst(BC, "Found call inst", Inst);1424    traceReg(BC, "Call destination reg", DestReg);1425    traceRegMask(BC, "SafeToDerefRegs", S.SafeToDerefRegs);1426  });1427  if (S.SafeToDerefRegs[DestReg])1428    return std::nullopt;1429 1430  return make_gadget_report(CallKind, Inst, DestReg);1431}1432 1433static std::optional<PartialReport<MCPhysReg>>1434shouldReportSigningOracle(const BinaryContext &BC, const MCInstReference &Inst,1435                          const SrcState &S) {1436  static const GadgetKind SigningOracleKind("signing oracle found");1437 1438  std::optional<MCPhysReg> SignedReg = BC.MIB->getSignedReg(Inst);1439  if (!SignedReg)1440    return std::nullopt;1441 1442  LLVM_DEBUG({1443    traceInst(BC, "Found sign inst", Inst);1444    traceReg(BC, "Signed reg", *SignedReg);1445    traceRegMask(BC, "TrustedRegs", S.TrustedRegs);1446  });1447  if (S.TrustedRegs[*SignedReg])1448    return std::nullopt;1449 1450  return make_gadget_report(SigningOracleKind, Inst, *SignedReg);1451}1452 1453static std::optional<PartialReport<MCPhysReg>>1454shouldReportAuthOracle(const BinaryContext &BC, const MCInstReference &Inst,1455                       const DstState &S) {1456  static const GadgetKind AuthOracleKind("authentication oracle found");1457 1458  bool IsChecked = false;1459  std::optional<MCPhysReg> AuthReg =1460      BC.MIB->getWrittenAuthenticatedReg(Inst, IsChecked);1461  if (!AuthReg || IsChecked)1462    return std::nullopt;1463 1464  LLVM_DEBUG({1465    traceInst(BC, "Found auth inst", Inst);1466    traceReg(BC, "Authenticated reg", *AuthReg);1467  });1468 1469  if (S.empty()) {1470    LLVM_DEBUG(dbgs() << "    DstState is empty!\n");1471    return make_generic_report(1472        Inst, "Warning: no state computed for an authentication instruction "1473              "(possibly unreachable)");1474  }1475 1476  LLVM_DEBUG(1477      { traceRegMask(BC, "safe output registers", S.CannotEscapeUnchecked); });1478  if (S.CannotEscapeUnchecked[*AuthReg])1479    return std::nullopt;1480 1481  return make_gadget_report(AuthOracleKind, Inst, *AuthReg);1482}1483 1484static SmallVector<MCPhysReg>1485collectRegsToTrack(ArrayRef<PartialReport<MCPhysReg>> Reports) {1486  SmallSet<MCPhysReg, 4> RegsToTrack;1487  for (auto Report : Reports)1488    if (Report.RequestedDetails)1489      RegsToTrack.insert(*Report.RequestedDetails);1490 1491  return SmallVector<MCPhysReg>(RegsToTrack.begin(), RegsToTrack.end());1492}1493 1494void FunctionAnalysisContext::findUnsafeUses(1495    SmallVector<PartialReport<MCPhysReg>> &Reports) {1496  auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, {});1497  LLVM_DEBUG(dbgs() << "Running src register safety analysis...\n");1498  Analysis->run();1499  LLVM_DEBUG({1500    dbgs() << "After src register safety analysis:\n";1501    BF.dump();1502  });1503 1504  bool UnreachableBBReported = false;1505  if (BF.hasCFG()) {1506    // Warn on basic blocks being unreachable according to BOLT (at most once1507    // per BinaryFunction), as this likely means the CFG reconstructed by BOLT1508    // is imprecise. A basic block can be1509    // * reachable from an entry basic block - a hopefully correct non-empty1510    //   state is propagated to that basic block sooner or later. All basic1511    //   blocks are expected to belong to this category under normal conditions.1512    // * reachable from a "directly unreachable" BB (a basic block that has no1513    //   direct predecessors and this is not because it is an entry BB) - *some*1514    //   non-empty state is propagated to this basic block sooner or later, as1515    //   the initial state of directly unreachable basic blocks is1516    //   pessimistically initialized to "all registers are unsafe"1517    //   - a warning can be printed for the "directly unreachable" basic block1518    // * neither reachable from an entry nor from a "directly unreachable" BB1519    //   (such as if this BB is in an isolated loop of basic blocks) - the final1520    //   state is computed to be empty for this basic block1521    //   - a warning can be printed for this basic block1522    for (BinaryBasicBlock &BB : BF) {1523      MCInst *FirstInst = BB.getFirstNonPseudoInstr();1524      // Skip empty basic block early for simplicity.1525      if (!FirstInst)1526        continue;1527 1528      bool IsDirectlyUnreachable = BB.pred_empty() && !BB.isEntryPoint();1529      bool HasNoStateComputed = Analysis->getStateBefore(*FirstInst).empty();1530      if (!IsDirectlyUnreachable && !HasNoStateComputed)1531        continue;1532 1533      // Arbitrarily attach the report to the first instruction of BB.1534      // This is printed as "[message] in function [name], basic block ...,1535      // at address ..." when the issue is reported to the user.1536      Reports.push_back(make_generic_report(1537          MCInstReference(BB, *FirstInst),1538          "Warning: possibly imprecise CFG, the analysis quality may be "1539          "degraded in this function. According to BOLT, unreachable code is "1540          "found" /* in function [name]... */));1541      UnreachableBBReported = true;1542      break; // One warning per function.1543    }1544  }1545  // FIXME: Warn the user about imprecise analysis when the function has no CFG1546  //        information at all.1547 1548  iterateOverInstrs(BF, [&](MCInstReference Inst) {1549    if (BC.MIB->isCFI(Inst))1550      return;1551 1552    const SrcState &S = Analysis->getStateBefore(Inst);1553    if (S.empty()) {1554      LLVM_DEBUG(traceInst(BC, "Instruction has no state, skipping", Inst));1555      assert(UnreachableBBReported && "Should be reported at least once");1556      (void)UnreachableBBReported;1557      return;1558    }1559 1560    if (auto Report = shouldReportReturnGadget(BC, Inst, S))1561      Reports.push_back(*Report);1562 1563    if (PacRetGadgetsOnly)1564      return;1565 1566    if (auto Report = shouldReportUnsafeTailCall(BC, BF, Inst, S))1567      Reports.push_back(*Report);1568 1569    if (auto Report = shouldReportCallGadget(BC, Inst, S))1570      Reports.push_back(*Report);1571    if (auto Report = shouldReportSigningOracle(BC, Inst, S))1572      Reports.push_back(*Report);1573  });1574}1575 1576void FunctionAnalysisContext::augmentUnsafeUseReports(1577    ArrayRef<PartialReport<MCPhysReg>> Reports) {1578  SmallVector<MCPhysReg> RegsToTrack = collectRegsToTrack(Reports);1579  // Re-compute the analysis with register tracking.1580  auto Analysis = SrcSafetyAnalysis::create(BF, AllocatorId, RegsToTrack);1581  LLVM_DEBUG(dbgs() << "\nRunning detailed src register safety analysis...\n");1582  Analysis->run();1583  LLVM_DEBUG({1584    dbgs() << "After detailed src register safety analysis:\n";1585    BF.dump();1586  });1587 1588  // Augment gadget reports.1589  for (auto &Report : Reports) {1590    MCInstReference Location = Report.Issue->Location;1591    LLVM_DEBUG(traceInst(BC, "Attaching clobbering info to", Location));1592    assert(Report.RequestedDetails &&1593           "Should be removed by handleSimpleReports");1594    auto DetailedInfo =1595        std::make_shared<ClobberingInfo>(Analysis->getLastClobberingInsts(1596            Location, BF, *Report.RequestedDetails));1597    Result.Diagnostics.emplace_back(Report.Issue, DetailedInfo);1598  }1599}1600 1601void FunctionAnalysisContext::findUnsafeDefs(1602    SmallVector<PartialReport<MCPhysReg>> &Reports) {1603  if (PacRetGadgetsOnly)1604    return;1605  if (AuthTrapsOnFailure)1606    return;1607 1608  auto Analysis = DstSafetyAnalysis::create(BF, AllocatorId, {});1609  LLVM_DEBUG(dbgs() << "Running dst register safety analysis...\n");1610  Analysis->run();1611  LLVM_DEBUG({1612    dbgs() << "After dst register safety analysis:\n";1613    BF.dump();1614  });1615 1616  iterateOverInstrs(BF, [&](MCInstReference Inst) {1617    if (BC.MIB->isCFI(Inst))1618      return;1619 1620    const DstState &S = Analysis->getStateAfter(Inst);1621 1622    if (auto Report = shouldReportAuthOracle(BC, Inst, S))1623      Reports.push_back(*Report);1624  });1625}1626 1627void FunctionAnalysisContext::augmentUnsafeDefReports(1628    ArrayRef<PartialReport<MCPhysReg>> Reports) {1629  SmallVector<MCPhysReg> RegsToTrack = collectRegsToTrack(Reports);1630  // Re-compute the analysis with register tracking.1631  auto Analysis = DstSafetyAnalysis::create(BF, AllocatorId, RegsToTrack);1632  LLVM_DEBUG(dbgs() << "\nRunning detailed dst register safety analysis...\n");1633  Analysis->run();1634  LLVM_DEBUG({1635    dbgs() << "After detailed dst register safety analysis:\n";1636    BF.dump();1637  });1638 1639  // Augment gadget reports.1640  for (auto &Report : Reports) {1641    MCInstReference Location = Report.Issue->Location;1642    LLVM_DEBUG(traceInst(BC, "Attaching leakage info to", Location));1643    assert(Report.RequestedDetails &&1644           "Should be removed by handleSimpleReports");1645    auto DetailedInfo = std::make_shared<LeakageInfo>(1646        Analysis->getLeakingInsts(Location, BF, *Report.RequestedDetails));1647    Result.Diagnostics.emplace_back(Report.Issue, DetailedInfo);1648  }1649}1650 1651void FunctionAnalysisContext::handleSimpleReports(1652    SmallVector<PartialReport<MCPhysReg>> &Reports) {1653  // Before re-running the detailed analysis, process the reports which do not1654  // need any additional details to be attached.1655  for (auto &Report : Reports) {1656    if (!Report.RequestedDetails)1657      Result.Diagnostics.emplace_back(Report.Issue, nullptr);1658  }1659  llvm::erase_if(Reports, [](const auto &R) { return !R.RequestedDetails; });1660}1661 1662void FunctionAnalysisContext::run() {1663  LLVM_DEBUG({1664    dbgs() << "Analyzing function " << BF.getPrintName()1665           << ", AllocatorId = " << AllocatorId << "\n";1666    BF.dump();1667  });1668 1669  SmallVector<PartialReport<MCPhysReg>> UnsafeUses;1670  findUnsafeUses(UnsafeUses);1671  handleSimpleReports(UnsafeUses);1672  if (!UnsafeUses.empty())1673    augmentUnsafeUseReports(UnsafeUses);1674 1675  SmallVector<PartialReport<MCPhysReg>> UnsafeDefs;1676  findUnsafeDefs(UnsafeDefs);1677  handleSimpleReports(UnsafeDefs);1678  if (!UnsafeDefs.empty())1679    augmentUnsafeDefReports(UnsafeDefs);1680}1681 1682void Analysis::runOnFunction(BinaryFunction &BF,1683                             MCPlusBuilder::AllocatorIdTy AllocatorId) {1684  FunctionAnalysisContext FA(BF, AllocatorId, PacRetGadgetsOnly);1685  FA.run();1686 1687  const FunctionAnalysisResult &FAR = FA.getResult();1688  if (FAR.Diagnostics.empty())1689    return;1690 1691  // `runOnFunction` is typically getting called from multiple threads in1692  // parallel. Therefore, use a lock to avoid data races when storing the1693  // result of the analysis in the `AnalysisResults` map.1694  {1695    std::lock_guard<std::mutex> Lock(AnalysisResultsMutex);1696    AnalysisResults[&BF] = FAR;1697  }1698}1699 1700static void printBB(const BinaryContext &BC, const BinaryBasicBlock &BB,1701                    size_t StartIndex = 0, size_t EndIndex = -1) {1702  if (EndIndex == (size_t)-1)1703    EndIndex = BB.size() - 1;1704  const BinaryFunction *BF = BB.getFunction();1705  for (unsigned I = StartIndex; I <= EndIndex; ++I) {1706    MCInstReference Inst(BB, I);1707    if (BC.MIB->isCFI(Inst))1708      continue;1709    BC.printInstruction(outs(), Inst, Inst.computeAddress(), BF);1710  }1711}1712 1713static void reportFoundGadgetInSingleBBSingleRelatedInst(1714    raw_ostream &OS, const BinaryContext &BC, const MCInstReference RelatedInst,1715    const MCInstReference Location) {1716  const BinaryBasicBlock *BB = Location.getBasicBlock();1717  assert(RelatedInst.hasCFG());1718  assert(Location.hasCFG());1719  if (BB == RelatedInst.getBasicBlock()) {1720    OS << "  This happens in the following basic block:\n";1721    printBB(BC, *BB);1722  }1723}1724 1725void Diagnostic::printBasicInfo(raw_ostream &OS, const BinaryContext &BC,1726                                StringRef IssueKind) const {1727  const BinaryBasicBlock *BB = Location.getBasicBlock();1728  const BinaryFunction *BF = Location.getFunction();1729  const uint64_t Address = Location.computeAddress();1730 1731  OS << "\nGS-PAUTH: " << IssueKind;1732  OS << " in function " << BF->getPrintName();1733  if (BB)1734    OS << ", basic block " << BB->getName();1735  OS << ", at address " << llvm::format("%x", Address) << "\n";1736  OS << "  The instruction is ";1737  BC.printInstruction(OS, Location, Address, BF);1738}1739 1740void GadgetDiagnostic::generateReport(raw_ostream &OS,1741                                      const BinaryContext &BC) const {1742  printBasicInfo(OS, BC, Kind.getDescription());1743}1744 1745static void printRelatedInstrs(raw_ostream &OS, const MCInstReference Location,1746                               ArrayRef<MCInstReference> RelatedInstrs) {1747  const BinaryFunction &BF = *Location.getFunction();1748  const BinaryContext &BC = BF.getBinaryContext();1749 1750  // Sort by address to ensure output is deterministic.1751  SmallVector<std::pair<uint64_t, MCInstReference>> RI;1752  for (auto &InstRef : RelatedInstrs)1753    RI.push_back(std::make_pair(InstRef.computeAddress(), InstRef));1754  llvm::sort(RI, [](auto A, auto B) { return A.first < B.first; });1755 1756  for (unsigned I = 0; I < RI.size(); ++I) {1757    auto [Address, InstRef] = RI[I];1758    OS << "  " << (I + 1) << ". ";1759    BC.printInstruction(OS, InstRef, Address, &BF);1760  };1761 1762  if (RelatedInstrs.size() == 1) {1763    const MCInstReference RelatedInst = RelatedInstrs[0];1764    // Printing the details for the MCInstReference::FunctionParent case1765    // is not implemented not to overcomplicate the code, as most functions1766    // are expected to have CFG information.1767    if (RelatedInst.hasCFG())1768      reportFoundGadgetInSingleBBSingleRelatedInst(OS, BC, RelatedInst,1769                                                   Location);1770  }1771}1772 1773void ClobberingInfo::print(raw_ostream &OS,1774                           const MCInstReference Location) const {1775  OS << "  The " << ClobberingInstrs.size()1776     << " instructions that write to the affected registers after any "1777        "authentication are:\n";1778  printRelatedInstrs(OS, Location, ClobberingInstrs);1779}1780 1781void LeakageInfo::print(raw_ostream &OS, const MCInstReference Location) const {1782  OS << "  The " << LeakingInstrs.size()1783     << " instructions that leak the affected registers are:\n";1784  printRelatedInstrs(OS, Location, LeakingInstrs);1785}1786 1787void GenericDiagnostic::generateReport(raw_ostream &OS,1788                                       const BinaryContext &BC) const {1789  printBasicInfo(OS, BC, Text);1790}1791 1792Error Analysis::runOnFunctions(BinaryContext &BC) {1793  ParallelUtilities::WorkFuncWithAllocTy WorkFun =1794      [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) {1795        runOnFunction(BF, AllocatorId);1796      };1797 1798  ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {1799    return false;1800  };1801 1802  ParallelUtilities::runOnEachFunctionWithUniqueAllocId(1803      BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,1804      SkipFunc, "PAuthGadgetScanner");1805 1806  for (BinaryFunction *BF : BC.getAllBinaryFunctions()) {1807    if (!AnalysisResults.count(BF))1808      continue;1809    for (const FinalReport &R : AnalysisResults[BF].Diagnostics) {1810      R.Issue->generateReport(outs(), BC);1811      if (R.Details)1812        R.Details->print(outs(), R.Issue->Location);1813    }1814  }1815  return Error::success();1816}1817 1818} // namespace PAuthGadgetScanner1819} // namespace bolt1820} // namespace llvm1821