[clang] [llvm] [LifetimeSafety] Reorganize code into modular components (PR #162474)

Gábor Horváth via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 9 07:01:47 PDT 2025


================
@@ -0,0 +1,143 @@
+//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LifetimeChecker, which detects use-after-free
+// errors by checking if live origins hold loans that have expired.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+
+namespace clang::lifetimes::internal {
+
+/// Struct to store the complete context for a potential lifetime violation.
+struct PendingWarning {
+  SourceLocation ExpiryLoc; // Where the loan expired.
+  const Expr *UseExpr;      // Where the origin holding this loan was used.
+  Confidence ConfidenceLevel;
+};
+
+class LifetimeChecker {
+private:
+  llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
+  LoanPropagationAnalysis &LoanPropagation;
+  LiveOriginAnalysis &LiveOrigins;
+  FactManager &FactMgr;
+  AnalysisDeclContext &ADC;
+  LifetimeSafetyReporter *Reporter;
+
+  void checkExpiry(const ExpireFact *EF);
+  void issuePendingWarnings();
+  static Confidence livenessKindToConfidence(LivenessKind K);
+
+public:
+  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
+                  FactManager &FM, AnalysisDeclContext &ADC,
+                  LifetimeSafetyReporter *Reporter);
+
+  void run();
+};
+
+LifetimeChecker::LifetimeChecker(LoanPropagationAnalysis &LPA,
+                                 LiveOriginAnalysis &LOA, FactManager &FM,
+                                 AnalysisDeclContext &ADC,
+                                 LifetimeSafetyReporter *Reporter)
+    : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
+      Reporter(Reporter) {}
+
+void LifetimeChecker::run() {
+  for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
+    for (const Fact *F : FactMgr.getFacts(B))
+      if (const auto *EF = F->getAs<ExpireFact>())
+        checkExpiry(EF);
+  issuePendingWarnings();
+}
+
+/// Checks for use-after-free errors when a loan expires.
+///
+/// This method examines all live origins at the expiry point and determines
+/// if any of them hold the expiring loan. If so, it creates a pending
+/// warning with the appropriate confidence level based on the liveness
+/// information. The confidence reflects whether the origin is definitely
+/// or maybe live at this point.
+///
+/// Note: This implementation considers only the confidence of origin
+/// liveness. Future enhancements could also consider the confidence of loan
+/// propagation (e.g., a loan may only be held on some execution paths).
+void LifetimeChecker::checkExpiry(const ExpireFact *EF) {
+  LoanID ExpiredLoan = EF->getLoanID();
+  LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
+  Confidence CurConfidence = Confidence::None;
+  const UseFact *BadUse = nullptr;
+  for (auto &[OID, LiveInfo] : Origins) {
+    LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
+    if (!HeldLoans.contains(ExpiredLoan))
+      continue;
+    // Loan is defaulted.
+    Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
+    if (CurConfidence < NewConfidence) {
+      CurConfidence = NewConfidence;
+      BadUse = LiveInfo.CausingUseFact;
+    }
+  }
+  if (!BadUse)
+    return;
+  // We have a use-after-free.
+  Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
+  if (LastConf >= CurConfidence)
+    return;
+  FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
+                                   /*UseExpr=*/BadUse->getUseExpr(),
+                                   /*ConfidenceLevel=*/CurConfidence};
+}
+
+Confidence LifetimeChecker::livenessKindToConfidence(LivenessKind K) {
+  switch (K) {
+  case LivenessKind::Must:
+    return Confidence::Definite;
+  case LivenessKind::Maybe:
+    return Confidence::Maybe;
+  case LivenessKind::Dead:
+    return Confidence::None;
+  }
+  llvm_unreachable("unknown liveness kind");
+}
+
+void LifetimeChecker::issuePendingWarnings() {
+  if (!Reporter)
+    return;
+  for (const auto &[LID, Warning] : FinalWarningsMap) {
+    const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
+    const Expr *IssueExpr = L.IssueExpr;
+    Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr, Warning.ExpiryLoc,
+                                 Warning.ConfidenceLevel);
+  }
+}
+
+void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation,
+                        LiveOriginAnalysis &LiveOrigins, FactManager &FactMgr,
+                        AnalysisDeclContext &ADC,
+                        LifetimeSafetyReporter *Reporter) {
+  llvm::TimeTraceScope TimeProfile("LifetimeChecker");
+  LifetimeChecker Checker(LoanPropagation, LiveOrigins, FactMgr, ADC, Reporter);
+  Checker.run();
+}
+
+} // namespace clang::lifetimes::internal
----------------
Xazax-hun wrote:

Nit missing new line.

https://github.com/llvm/llvm-project/pull/162474


More information about the llvm-commits mailing list