Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7a21c40
[LifetimeSafety] Make the dataflow analysis generic (#148222)
usx95 Jul 16, 2025
093fe20
[LifetimeSafety] Support bidirectional dataflow analysis (#148967)
usx95 Jul 16, 2025
7067c95
[LifetimeSafety] Add per-program-point lattice tracking (#149199)
usx95 Jul 21, 2025
e65ffb6
Reapply "[LifetimeSafety] Revamp test suite using unittests (#149158)"
usx95 Jul 22, 2025
01e7a2d
[LifetimeSafety] Add loan expiry analysis (#148712)
usx95 Jul 23, 2025
99e0f66
[LifetimeSafety] Handle pruned-edges (null blocks) in dataflow (#150670)
usx95 Aug 3, 2025
ea493b9
[LifetimeSafety] Implement a basic use-after-free diagnostic (#149731)
usx95 Aug 18, 2025
5200cdb
[LifetimeSafety] Enhance benchmark script for new sub analyses (#149577)
usx95 Aug 18, 2025
c163bef
[LifetimeSafety] Improve Origin information in debug output (#153951)
usx95 Aug 19, 2025
2f37c67
[LifetimeSafety] Fix duplicate loan generation for ImplicitCastExpr (…
usx95 Sep 3, 2025
f6ffb7d
[LifetimeSafety] Mark all DeclRefExpr as usages of the corresp. origi…
usx95 Sep 5, 2025
d3b3a7c
[LifetimeSafety] Add PR labeler automation (#157820)
usx95 Sep 10, 2025
815ff9b
[LifetimeSafety] Associate origins to all l-valued expressions (#156896)
usx95 Sep 11, 2025
339d0db
[LifetimeSafety] Add support for GSL Pointer types (#154009)
usx95 Sep 14, 2025
7f403fb
[LifetimeSafety] Avoid adding already present items in sets/maps (#15…
usx95 Sep 19, 2025
01fe322
[LifetimeSafety] Disable canonicalization in immutable collections (#…
usx95 Sep 25, 2025
176a9b0
[LifetimeSafety] Implement support for lifetimebound attribute (#158489)
usx95 Sep 25, 2025
34a8bab
[LifetimeSafety] Introduce a liveness-based lifetime policy (#159991)
usx95 Oct 7, 2025
0ca9dba
[LifetimeSafety] Reorganize code into modular components (#162474)
usx95 Oct 10, 2025
0f9a415
Revert "[LifetimeSafety] Reorganize code into modular components (#16…
shiltian Oct 10, 2025
255a6dd
Reapply "[LifetimeSafety] Reorganize code into modular components (#1…
usx95 Oct 11, 2025
5119f73
[LifetimeSafety] Fix unittest shared build. NFC
darkbuck Oct 11, 2025
e055ca5
[LifetimeSafety] Fix a crash caused by nullptr to llvm::isa
usx95 Oct 14, 2025
2884795
[LifetimeSafety] Fix Python path for Windows compatibility (#166291)
usx95 Nov 4, 2025
ded2476
[LifetimeSafety] Optimize loan propagation by separating persistent a…
usx95 Nov 7, 2025
f3a92d6
[LifetimeSafety] Fix typo which breaks the test
usx95 Nov 7, 2025
10492e4
[LifetimeSafety] Optimize fact storage with IDs and vector-based look…
usx95 Nov 7, 2025
68b6c60
[LifetimeSafety] Use StringMap::contains (NFC) (#167186)
kazutakahirata Nov 9, 2025
9786377
[LifetimeSafety] Add support for conditional operators (#167240)
usx95 Nov 12, 2025
a6beb12
[LifetimeSafety] Ignore parentheses when tracking expressions (#167245)
usx95 Nov 12, 2025
33d1123
[LifetimeSafety] Detect use-after-return (#165370)
kashika0112 Nov 19, 2025
361f176
[LifetimeSafety] Detect expiry of loans to trivially destructed types…
kashika0112 Nov 21, 2025
fadaf5d
[LifetimeSafety] Add parameter lifetime tracking in CFG (#169320)
usx95 Nov 25, 2025
978ce83
[LifetimeSafety] Move GSL pointer/owner type detection to LifetimeAnn…
usx95 Nov 26, 2025
1eaef91
[LifetimeSafety] Suggest lifetime annotations (#169767)
kashika0112 Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/new-prs-labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,14 @@ clang:openmp:
- llvm/unittests/Frontend/OpenMP*
- llvm/test/Transforms/OpenMP/**

clang:temporal-safety:
- clang/include/clang/Analysis/Analyses/LifetimeSafety/**
- clang/lib/Analysis/LifetimeSafety/**
- clang/unittests/Analysis/LifetimeSafety*
- clang/test/Sema/*lifetime-safety*
- clang/test/Sema/*lifetime-analysis*
- clang/test/Analysis/LifetimeSafety/**

clang:as-a-library:
- clang/tools/libclang/**
- clang/bindings/**
Expand Down
30 changes: 0 additions & 30 deletions clang/include/clang/Analysis/Analyses/LifetimeSafety.h

This file was deleted.

35 changes: 35 additions & 0 deletions clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===- Checker.h - C++ Lifetime Safety Analysis -*----------- 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 defines and enforces the lifetime safety policy. It detects
// use-after-free errors by examining loan expiration points and checking if
// any live origins hold the expired loans.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H

#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"

namespace clang::lifetimes::internal {

/// Runs the lifetime checker, which detects use-after-free errors by
/// examining loan expiration points and checking if any live origins hold
/// the expired loan.
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
const LiveOriginsAnalysis &LiveOrigins,
const FactManager &FactMgr, AnalysisDeclContext &ADC,
LifetimeSafetyReporter *Reporter);

} // namespace clang::lifetimes::internal

#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
252 changes: 252 additions & 0 deletions clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
//===- Facts.h - Lifetime Analysis Facts and Fact Manager ------*- 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 defines Facts, which are atomic lifetime-relevant events (such as
// loan issuance, loan expiration, origin flow, and use), and the FactManager,
// which manages the storage and retrieval of facts for each CFG block.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H

#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
#include <cstdint>

namespace clang::lifetimes::internal {

using FactID = utils::ID<struct FactTag>;

/// An abstract base class for a single, atomic lifetime-relevant event.
class Fact {

public:
enum class Kind : uint8_t {
/// A new loan is issued from a borrow expression (e.g., &x).
Issue,
/// A loan expires as its underlying storage is freed (e.g., variable goes
/// out of scope).
Expire,
/// An origin is propagated from a source to a destination (e.g., p = q).
/// This can also optionally kill the destination origin before flowing into
/// it. Otherwise, the source's loan set is merged into the destination's
/// loan set.
OriginFlow,
/// An origin is used (eg. appears as l-value expression like DeclRefExpr).
Use,
/// A marker for a specific point in the code, for testing.
TestPoint,
/// An origin that escapes the function scope (e.g., via return).
OriginEscapes,
};

private:
Kind K;
FactID ID;

protected:
Fact(Kind K) : K(K) {}

public:
virtual ~Fact() = default;
Kind getKind() const { return K; }

void setID(FactID ID) { this->ID = ID; }
FactID getID() const { return ID; }

template <typename T> const T *getAs() const {
if (T::classof(this))
return static_cast<const T *>(this);
return nullptr;
}

virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &) const;
};

/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
/// `Fact`. identified by a lifetime-related event (`Fact`).
///
/// A `ProgramPoint` has "after" semantics: it represents the location
/// immediately after its corresponding `Fact`.
using ProgramPoint = const Fact *;

class IssueFact : public Fact {
LoanID LID;
OriginID OID;

public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }

IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
LoanID getLoanID() const { return LID; }
OriginID getOriginID() const { return OID; }
void dump(llvm::raw_ostream &OS, const LoanManager &LM,
const OriginManager &OM) const override;
};

class ExpireFact : public Fact {
LoanID LID;
SourceLocation ExpiryLoc;

public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }

ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
: Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}

LoanID getLoanID() const { return LID; }
SourceLocation getExpiryLoc() const { return ExpiryLoc; }

void dump(llvm::raw_ostream &OS, const LoanManager &LM,
const OriginManager &) const override;
};

class OriginFlowFact : public Fact {
OriginID OIDDest;
OriginID OIDSrc;
// True if the destination origin should be killed (i.e., its current loans
// cleared) before the source origin's loans are flowed into it.
bool KillDest;

public:
static bool classof(const Fact *F) {
return F->getKind() == Kind::OriginFlow;
}

OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
: Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
KillDest(KillDest) {}

OriginID getDestOriginID() const { return OIDDest; }
OriginID getSrcOriginID() const { return OIDSrc; }
bool getKillDest() const { return KillDest; }

void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const override;
};

class OriginEscapesFact : public Fact {
OriginID OID;
const Expr *EscapeExpr;

public:
static bool classof(const Fact *F) {
return F->getKind() == Kind::OriginEscapes;
}

OriginEscapesFact(OriginID OID, const Expr *EscapeExpr)
: Fact(Kind::OriginEscapes), OID(OID), EscapeExpr(EscapeExpr) {}
OriginID getEscapedOriginID() const { return OID; }
const Expr *getEscapeExpr() const { return EscapeExpr; };
void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const override;
};

class UseFact : public Fact {
const Expr *UseExpr;
OriginID OID;
// True if this use is a write operation (e.g., left-hand side of assignment).
// Write operations are exempted from use-after-free checks.
bool IsWritten = false;

public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }

UseFact(const Expr *UseExpr, OriginManager &OM)
: Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}

OriginID getUsedOrigin() const { return OID; }
const Expr *getUseExpr() const { return UseExpr; }
void markAsWritten() { IsWritten = true; }
bool isWritten() const { return IsWritten; }

void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const override;
};

/// A dummy-fact used to mark a specific point in the code for testing.
/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
class TestPointFact : public Fact {
StringRef Annotation;

public:
static bool classof(const Fact *F) { return F->getKind() == Kind::TestPoint; }

explicit TestPointFact(StringRef Annotation)
: Fact(Kind::TestPoint), Annotation(Annotation) {}

StringRef getAnnotation() const { return Annotation; }

void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &) const override;
};

class FactManager {
public:
void init(const CFG &Cfg) {
assert(BlockToFacts.empty() && "FactManager already initialized");
BlockToFacts.resize(Cfg.getNumBlockIDs());
}

llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
return BlockToFacts[B->getBlockID()];
}

void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
if (!NewFacts.empty())
BlockToFacts[B->getBlockID()].assign(NewFacts.begin(), NewFacts.end());
}

template <typename FactType, typename... Args>
FactType *createFact(Args &&...args) {
void *Mem = FactAllocator.Allocate<FactType>();
FactType *Res = new (Mem) FactType(std::forward<Args>(args)...);
Res->setID(NextFactID++);
return Res;
}

void dump(const CFG &Cfg, AnalysisDeclContext &AC) const;

/// Retrieves program points that were specially marked in the source code
/// for testing.
///
/// The analysis recognizes special function calls of the form
/// `void("__lifetime_test_point_<name>")` as test points. This method returns
/// a map from the annotation string (<name>) to the corresponding
/// `ProgramPoint`. This allows test harnesses to query the analysis state at
/// user-defined locations in the code.
/// \note This is intended for testing only.
llvm::StringMap<ProgramPoint> getTestPoints() const;
/// Retrieves all the facts in the block containing Program Point P.
/// \note This is intended for testing only.
llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;

unsigned getNumFacts() const { return NextFactID.Value; }

LoanManager &getLoanMgr() { return LoanMgr; }
const LoanManager &getLoanMgr() const { return LoanMgr; }
OriginManager &getOriginMgr() { return OriginMgr; }
const OriginManager &getOriginMgr() const { return OriginMgr; }

private:
FactID NextFactID{0};
LoanManager LoanMgr;
OriginManager OriginMgr;
/// Facts for each CFG block, indexed by block ID.
llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts;
llvm::BumpPtrAllocator FactAllocator;
};
} // namespace clang::lifetimes::internal

#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
Loading