2323#include " llvm/Support/Debug.h"
2424#include " llvm/Support/TimeProfiler.h"
2525#include < cstdint>
26+ #include < memory>
2627
2728namespace clang ::lifetimes {
2829namespace internal {
29- namespace {
30- template <typename Tag>
31- inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
32- return OS << ID.Value ;
33- }
34- } // namespace
3530
3631// / Represents the storage location being borrowed, e.g., a specific stack
3732// / variable.
@@ -832,6 +827,91 @@ class LoanPropagationAnalysis
832827 }
833828};
834829
830+ // ========================================================================= //
831+ // Expired Loans Analysis
832+ // ========================================================================= //
833+
834+ // / The dataflow lattice for tracking the set of expired loans.
835+ struct ExpiredLattice {
836+ LoanSet Expired;
837+
838+ ExpiredLattice () : Expired(nullptr ) {};
839+ explicit ExpiredLattice (LoanSet S) : Expired(S) {}
840+
841+ bool operator ==(const ExpiredLattice &Other) const {
842+ return Expired == Other.Expired ;
843+ }
844+ bool operator !=(const ExpiredLattice &Other) const {
845+ return !(*this == Other);
846+ }
847+
848+ void dump (llvm::raw_ostream &OS) const {
849+ OS << " ExpiredLattice State:\n " ;
850+ if (Expired.isEmpty ())
851+ OS << " <empty>\n " ;
852+ for (const LoanID &LID : Expired)
853+ OS << " Loan " << LID << " is expired\n " ;
854+ }
855+ };
856+
857+ // / The analysis that tracks which loans have expired.
858+ class ExpiredLoansAnalysis
859+ : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
860+ Direction::Forward> {
861+
862+ LoanSet::Factory &Factory;
863+
864+ public:
865+ ExpiredLoansAnalysis (const CFG &C, AnalysisDeclContext &AC, FactManager &F,
866+ LifetimeFactory &Factory)
867+ : DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {}
868+
869+ using Base::transfer;
870+
871+ StringRef getAnalysisName () const { return " ExpiredLoans" ; }
872+
873+ Lattice getInitialState () { return Lattice (Factory.getEmptySet ()); }
874+
875+ // / Merges two lattices by taking the union of the expired loan sets.
876+ Lattice join (Lattice L1, Lattice L2) const {
877+ return Lattice (utils::join (L1.Expired , L2.Expired , Factory));
878+ }
879+
880+ Lattice transfer (Lattice In, const ExpireFact &F) {
881+ return Lattice (Factory.add (In.Expired , F.getLoanID ()));
882+ }
883+
884+ // Removes the loan from the set of expired loans.
885+ //
886+ // When a loan is re-issued (e.g., in a loop), it is no longer considered
887+ // expired. A loan can be in the expired set at the point of issue due to
888+ // the dataflow state from a previous loop iteration being propagated along
889+ // a backedge in the CFG.
890+ //
891+ // Note: This has a subtle false-negative though where a loan from previous
892+ // iteration is not overwritten by a reissue. This needs careful tracking
893+ // of loans "across iterations" which can be considered for future
894+ // enhancements.
895+ //
896+ // void foo(int safe) {
897+ // int* p = &safe;
898+ // int* q = &safe;
899+ // while (condition()) {
900+ // int x = 1;
901+ // p = &x; // A loan to 'x' is issued to 'p' in every iteration.
902+ // if (condition()) {
903+ // q = p;
904+ // }
905+ // (void)*p; // OK — 'p' points to 'x' from new iteration.
906+ // (void)*q; // UaF - 'q' still points to 'x' from previous iteration
907+ // // which is now destroyed.
908+ // }
909+ // }
910+ Lattice transfer (Lattice In, const IssueFact &F) {
911+ return Lattice (Factory.remove (In.Expired , F.getLoanID ()));
912+ }
913+ };
914+
835915// ========================================================================= //
836916// TODO:
837917// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)`
@@ -873,6 +953,10 @@ void LifetimeSafetyAnalysis::run() {
873953 LoanPropagation =
874954 std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
875955 LoanPropagation->run ();
956+
957+ ExpiredLoans =
958+ std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
959+ ExpiredLoans->run ();
876960}
877961
878962LoanSet LifetimeSafetyAnalysis::getLoansAtPoint (OriginID OID,
@@ -881,6 +965,11 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
881965 return LoanPropagation->getLoans (OID, PP);
882966}
883967
968+ LoanSet LifetimeSafetyAnalysis::getExpiredLoansAtPoint (ProgramPoint PP) const {
969+ assert (ExpiredLoans && " ExpiredLoansAnalysis has not been run." );
970+ return ExpiredLoans->getState (PP).Expired ;
971+ }
972+
884973std::optional<OriginID>
885974LifetimeSafetyAnalysis::getOriginIDForDecl (const ValueDecl *D) const {
886975 assert (FactMgr && " FactManager not initialized" );
0 commit comments