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