diff options
Diffstat (limited to 'clang/lib/Analysis')
-rw-r--r-- | clang/lib/Analysis/LifetimeSafety.cpp | 101 |
1 files changed, 95 insertions, 6 deletions
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index ae6ec9f..94b8197 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -23,15 +23,10 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/TimeProfiler.h" #include <cstdint> +#include <memory> namespace clang::lifetimes { namespace internal { -namespace { -template <typename Tag> -inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) { - return OS << ID.Value; -} -} // namespace /// Represents the storage location being borrowed, e.g., a specific stack /// variable. @@ -833,6 +828,91 @@ private: }; // ========================================================================= // +// Expired Loans Analysis +// ========================================================================= // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { + return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { + return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { + OS << "ExpiredLattice State:\n"; + if (Expired.isEmpty()) + OS << " <empty>\n"; + for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis + : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice, + Direction::Forward> { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &Factory) + : DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {} + + using Base::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { + return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { + return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + // Removes the loan from the set of expired loans. + // + // When a loan is re-issued (e.g., in a loop), it is no longer considered + // expired. A loan can be in the expired set at the point of issue due to + // the dataflow state from a previous loop iteration being propagated along + // a backedge in the CFG. + // + // Note: This has a subtle false-negative though where a loan from previous + // iteration is not overwritten by a reissue. This needs careful tracking + // of loans "across iterations" which can be considered for future + // enhancements. + // + // void foo(int safe) { + // int* p = &safe; + // int* q = &safe; + // while (condition()) { + // int x = 1; + // p = &x; // A loan to 'x' is issued to 'p' in every iteration. + // if (condition()) { + // q = p; + // } + // (void)*p; // OK — 'p' points to 'x' from new iteration. + // (void)*q; // UaF - 'q' still points to 'x' from previous iteration + // // which is now destroyed. + // } + // } + Lattice transfer(Lattice In, const IssueFact &F) { + return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + +// ========================================================================= // // TODO: // - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` // - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` @@ -873,6 +953,10 @@ void LifetimeSafetyAnalysis::run() { LoanPropagation = std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory); LoanPropagation->run(); + + ExpiredLoans = + std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory); + ExpiredLoans->run(); } LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID, @@ -881,6 +965,11 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID, return LoanPropagation->getLoans(OID, PP); } +LoanSet LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const { + assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run."); + return ExpiredLoans->getState(PP).Expired; +} + std::optional<OriginID> LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const { assert(FactMgr && "FactManager not initialized"); |