https://github.com/usx95 created 
https://github.com/llvm/llvm-project/pull/179093

None

>From 7d661e812adfc2359b0878f6e4279bfd95e43ebd Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <[email protected]>
Date: Sat, 31 Jan 2026 21:48:00 +0000
Subject: [PATCH] use-after-invalidation

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 21 +++++++
 .../LifetimeSafety/LifetimeAnnotations.h      |  4 ++
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  4 ++
 clang/include/clang/Basic/DiagnosticGroups.td | 10 ++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  6 ++
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 62 +++++++++++++++++--
 clang/lib/Analysis/LifetimeSafety/Dataflow.h  |  3 +
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  7 +++
 .../LifetimeSafety/FactsGenerator.cpp         | 10 +++
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 36 +++++++++++
 .../LifetimeSafety/LoanPropagation.cpp        |  1 +
 clang/lib/Sema/AnalysisBasedWarnings.cpp      | 11 ++++
 12 files changed, 169 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 14ff37cf296b8..43f1f065a6605 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -51,6 +51,8 @@ class Fact {
     TestPoint,
     /// An origin that escapes the function scope (e.g., via return).
     OriginEscapes,
+    /// An origin is invalidated (e.g. vector resized).
+    InvalidateOrigin,
   };
 
 private:
@@ -222,6 +224,25 @@ class UseFact : public Fact {
             const OriginManager &OM) const override;
 };
 
+class InvalidateOriginFact : public Fact {
+  OriginID OID;
+  const Expr *InvalidationExpr;
+
+public:
+  static bool classof(const Fact *F) {
+    return F->getKind() == Kind::InvalidateOrigin;
+  }
+
+  InvalidateOriginFact(OriginID OID, const Expr *InvalidationExpr)
+      : Fact(Kind::InvalidateOrigin), OID(OID),
+        InvalidationExpr(InvalidationExpr) {}
+
+  OriginID getInvalidatedOrigin() const { return OID; }
+  const Expr *getInvalidationExpr() const { return InvalidationExpr; }
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override;
+};
+
 /// Top-level origin of the expression which was found to be moved, e.g, when
 /// being used as an argument to an r-value reference parameter.
 class MovedOriginFact : public Fact {
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 3ca7a79d32ee1..5cc562a482bb0 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -66,6 +66,10 @@ bool isGslPointerType(QualType QT);
 // Tells whether the type is annotated with [[gsl::Owner]].
 bool isGslOwnerType(QualType QT);
 
+// Returns true if the given method invalidates iterators or references to
+// container elements.
+bool isContainerInvalidationMethod(const CXXMethodDecl *MD);
+
 } // namespace clang::lifetimes
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 6e87951c8961d..3311c592b21ee 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -74,6 +74,10 @@ class LifetimeSafetySemaHelper {
                                    const Expr *MovedExpr,
                                    SourceLocation ExpiryLoc) {}
 
+  virtual void reportUseAfterInvalidation(const Expr *IssueExpr,
+                                          const Expr *UseExpr,
+                                          const Expr *InvalidationExpr) {}
+
   // Suggests lifetime bound annotations for function paramters.
   virtual void suggestLifetimeboundToParmVar(SuggestionScope Scope,
                                              const ParmVarDecl *ParmToAnnotate,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 2f128fda5e31f..270b451344092 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -546,8 +546,16 @@ def LifetimeSafetyDanglingFieldStrict : 
DiagGroup<"lifetime-safety-dangling-fiel
   }];
 }
 
+
+def LifetimeSafetyInvalidation : DiagGroup<"lifetime-safety-invalidation"> {
+  code Documentation = [{
+    Warning to detect invalidation of references.
+  }];
+}
+
 def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive",
-                                         [LifetimeSafetyDanglingField]>;
+                                         [LifetimeSafetyDanglingField,
+                                         LifetimeSafetyInvalidation]>;
 def LifetimeSafetyStrict : DiagGroup<"lifetime-safety-strict",
                                     [LifetimeSafetyDanglingFieldStrict]>;
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c6484295504a4..b72bdfb175b12 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10832,6 +10832,11 @@ def warn_lifetime_safety_return_stack_addr_moved_strict
       InGroup<LifetimeSafetyStrict>,
       DefaultIgnore;
 
+def warn_lifetime_safety_invalidation
+    : Warning<"object whose reference is captured is later invalidated">,
+      InGroup<LifetimeSafetyInvalidation>,
+      DefaultIgnore;
+
 def warn_lifetime_safety_dangling_field
     : Warning<"address of stack memory escapes to a field">,
       InGroup<LifetimeSafetyDanglingField>,
@@ -10844,6 +10849,7 @@ def warn_lifetime_safety_dangling_field_moved
       DefaultIgnore;
 
 def note_lifetime_safety_used_here : Note<"later used here">;
+def note_lifetime_safety_invalidated_here : Note<"invalidated here">;
 def note_lifetime_safety_destroyed_here : Note<"destroyed here">;
 def note_lifetime_safety_returned_here : Note<"returned here">;
 def note_lifetime_safety_moved_here : Note<"potentially moved here">;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 59ae665e652a6..b73ce8ef619d2 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -24,6 +24,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/TimeProfiler.h"
 
@@ -48,6 +49,7 @@ struct PendingWarning {
   SourceLocation ExpiryLoc; // Where the loan expired.
   llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
   const Expr *MovedExpr;
+  const Expr *InvalidatedByExpr;
   Confidence ConfidenceLevel;
 };
 
@@ -80,6 +82,8 @@ class LifetimeChecker {
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
           checkExpiry(EF);
+        else if (const auto *IOF = F->getAs<InvalidateOriginFact>())
+          checkInvalidation(IOF);
         else if (const auto *OEF = F->getAs<OriginEscapesFact>())
           checkAnnotations(OEF);
     issuePendingWarnings();
@@ -175,9 +179,53 @@ class LifetimeChecker {
     FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
                                      /*BestCausingFact=*/BestCausingFact,
                                      /*MovedExpr=*/MovedExpr,
+                                     /*InvalidatedByExpr=*/nullptr,
                                      /*ConfidenceLevel=*/CurConfidence};
   }
 
+  void checkInvalidation(const InvalidateOriginFact *IOF) {
+    OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
+    LoanSet DirectlyInvalidatedLoans =
+        LoanPropagation.getLoans(InvalidatedOrigin, IOF);
+    llvm::DenseSet<LoanID> AllInvalidatedLoans;
+
+    FactMgr.forAllPredecessors(IOF, [&](const Fact *PF) {
+      auto *IF = PF->getAs<IssueFact>();
+      if (!IF)
+        return;
+      for (LoanID LID : DirectlyInvalidatedLoans) {
+        const Loan *InvalidatedLoan = FactMgr.getLoanMgr().getLoan(LID);
+        auto *PL = dyn_cast<PathLoan>(InvalidatedLoan);
+        if (!PL)
+          continue;
+        LoanID ReachableLID = IF->getLoanID();
+        const Loan *ReachableLoan = FactMgr.getLoanMgr().getLoan(ReachableLID);
+        if (auto *RL = dyn_cast<PathLoan>(ReachableLoan))
+          if (RL->getAccessPath() == PL->getAccessPath())
+            AllInvalidatedLoans.insert(ReachableLID);
+      }
+    });
+    LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
+    for (auto &[OID, LiveInfo] : Origins) {
+      LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
+      for (LoanID HeldLID : HeldLoans) {
+        if (AllInvalidatedLoans.count(HeldLID)) {
+          Confidence CurConfidence = livenessKindToConfidence(LiveInfo.Kind);
+          Confidence LastConf =
+              FinalWarningsMap.lookup(HeldLID).ConfidenceLevel;
+          if (LastConf < CurConfidence) {
+            FinalWarningsMap[HeldLID] = {
+                /*ExpiryLoc=*/{},
+                /*CausingFact=*/LiveInfo.CausingFact,
+                /*MovedExpr=*/nullptr,
+                /*InvalidatedByExpr=*/IOF->getInvalidationExpr(),
+                /*ConfidenceLevel=*/CurConfidence};
+          }
+        }
+      }
+    }
+  }
+
   void issuePendingWarnings() {
     if (!SemaHelper)
       return;
@@ -191,11 +239,15 @@ class LifetimeChecker {
       const Expr *MovedExpr = Warning.MovedExpr;
       SourceLocation ExpiryLoc = Warning.ExpiryLoc;
 
-      if (const auto *UF = CausingFact.dyn_cast<const UseFact *>())
-        SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
-                                       ExpiryLoc, Confidence);
-      else if (const auto *OEF =
-                   CausingFact.dyn_cast<const OriginEscapesFact *>()) {
+      if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) {
+        if (Warning.InvalidatedByExpr)
+          SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
+                                                 Warning.InvalidatedByExpr);
+        else
+          SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), 
MovedExpr,
+                                         ExpiryLoc, Confidence);
+      } else if (const auto *OEF =
+                     CausingFact.dyn_cast<const OriginEscapesFact *>()) {
         if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
           SemaHelper->reportUseAfterReturn(IssueExpr,
                                            RetEscape->getReturnExpr(),
diff --git a/clang/lib/Analysis/LifetimeSafety/Dataflow.h 
b/clang/lib/Analysis/LifetimeSafety/Dataflow.h
index 4de9bbb54af9f..0f64ac8a36ef7 100644
--- a/clang/lib/Analysis/LifetimeSafety/Dataflow.h
+++ b/clang/lib/Analysis/LifetimeSafety/Dataflow.h
@@ -178,6 +178,8 @@ class DataflowAnalysis {
       return D->transfer(In, *F->getAs<UseFact>());
     case Fact::Kind::TestPoint:
       return D->transfer(In, *F->getAs<TestPointFact>());
+    case Fact::Kind::InvalidateOrigin:
+      return D->transfer(In, *F->getAs<InvalidateOriginFact>());
     }
     llvm_unreachable("Unknown fact kind");
   }
@@ -190,6 +192,7 @@ class DataflowAnalysis {
   Lattice transfer(Lattice In, const OriginEscapesFact &) { return In; }
   Lattice transfer(Lattice In, const UseFact &) { return In; }
   Lattice transfer(Lattice In, const TestPointFact &) { return In; }
+  Lattice transfer(Lattice In, const InvalidateOriginFact &) { return In; }
 };
 } // namespace clang::lifetimes::internal
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 7b22634a51947..7be9eddddabe9 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -82,6 +82,13 @@ void UseFact::dump(llvm::raw_ostream &OS, const LoanManager 
&,
   OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
 }
 
+void InvalidateOriginFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                                const OriginManager &OM) const {
+  OS << "InvalidateOrigin (";
+  OM.dump(getInvalidatedOrigin(), OS);
+  OS << ")\n";
+}
+
 void TestPointFact::dump(llvm::raw_ostream &OS, const LoanManager &,
                          const OriginManager &) const {
   OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index bdb48b0c81172..f9018faeb125d 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -551,6 +551,16 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
   FD = getDeclWithMergedLifetimeBoundAttrs(FD);
   if (!FD)
     return;
+
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+      MD && MD->isInstance() && isContainerInvalidationMethod(MD)) {
+    OriginList *ThisList = getOriginsList(*Args[0]);
+    if (ThisList) {
+      CurrentBlockFacts.push_back(FactMgr.createFact<InvalidateOriginFact>(
+          ThisList->getOuterOriginID(), Call));
+    }
+  }
+
   handleMovedArgsInCall(FD, Args);
   if (!CallList)
     return;
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index be33caf327802..0c8ab72e54a02 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -227,4 +227,40 @@ template <typename T> static bool 
isRecordWithAttr(QualType Type) {
 bool isGslPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); 
}
 bool isGslOwnerType(QualType QT) { return isRecordWithAttr<OwnerAttr>(QT); }
 
+bool isContainerInvalidationMethod(const CXXMethodDecl *MD) {
+  if (!MD)
+    return false;
+
+  const CXXRecordDecl *RD = MD->getParent();
+  if (!RD || !isInStlNamespace(RD))
+    return false;
+
+  StringRef ContainerName;
+  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+    ContainerName = CTSD->getSpecializedTemplate()->getName();
+  } else if (RD->getIdentifier()) {
+    ContainerName = RD->getName();
+  } else {
+    return false;
+  }
+
+  bool IsSequenceContainer = ContainerName == "vector" ||
+                             ContainerName == "basic_string" ||
+                             ContainerName == "deque";
+  bool IsAssociativeContainer =
+      ContainerName == "set" || ContainerName == "multiset" ||
+      ContainerName == "map" || ContainerName == "multimap" ||
+      ContainerName == "unordered_set" || ContainerName == 
"unordered_multiset" ||
+      ContainerName == "unordered_map" || ContainerName == 
"unordered_multimap";
+
+  if (!IsSequenceContainer && !IsAssociativeContainer)
+    return false;
+
+  static const llvm::StringSet<> InvalidatingMembers = {
+      "push_back", "emplace_back", "insert",   "erase", "resize",
+      "clear",     "emplace",      "pop_back", "swap"};
+
+  return MD->getIdentifier() && InvalidatingMembers.contains(MD->getName());
+}
+
 } // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 0996d11f0cdeb..8a020eb829be6 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -67,6 +67,7 @@ static llvm::BitVector computePersistentOrigins(const 
FactManager &FactMgr,
       case Fact::Kind::OriginEscapes:
       case Fact::Kind::Expire:
       case Fact::Kind::TestPoint:
+      case Fact::Kind::InvalidateOrigin:
         break;
       }
     }
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 858d5b7f67b58..d2681dd3ca6f6 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2925,6 +2925,17 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         << DanglingField->getEndLoc();
   }
 
+  void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
+                                  const Expr *InvalidationExpr) override {
+    S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidation)
+        << IssueExpr->getSourceRange();
+    S.Diag(InvalidationExpr->getExprLoc(),
+           diag::note_lifetime_safety_invalidated_here)
+        << InvalidationExpr->getSourceRange();
+    S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
+        << UseExpr->getSourceRange();
+  }
+
   void suggestLifetimeboundToParmVar(SuggestionScope Scope,
                                      const ParmVarDecl *ParmToAnnotate,
                                      const Expr *EscapeExpr) override {

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to