Skip to content

Commit 249a819

Browse files
authored
Merge pull request #11947 from Xazax-hun/gaborh/lifetime-analysis-on-21.x
Cherry pick lifetime analysis work from upstream
2 parents 6526622 + 382e9df commit 249a819

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+6395
-1202
lines changed

.github/new-prs-labeler.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,14 @@ clang:openmp:
10821082
- llvm/unittests/Frontend/OpenMP*
10831083
- llvm/test/Transforms/OpenMP/**
10841084

1085+
clang:temporal-safety:
1086+
- clang/include/clang/Analysis/Analyses/LifetimeSafety/**
1087+
- clang/lib/Analysis/LifetimeSafety/**
1088+
- clang/unittests/Analysis/LifetimeSafety*
1089+
- clang/test/Sema/*lifetime-safety*
1090+
- clang/test/Sema/*lifetime-analysis*
1091+
- clang/test/Analysis/LifetimeSafety/**
1092+
10851093
clang:as-a-library:
10861094
- clang/tools/libclang/**
10871095
- clang/bindings/**

clang/include/clang/Analysis/Analyses/LifetimeSafety.h

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===- Checker.h - C++ Lifetime Safety Analysis -*----------- C++-*-=========//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines and enforces the lifetime safety policy. It detects
10+
// use-after-free errors by examining loan expiration points and checking if
11+
// any live origins hold the expired loans.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
16+
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
17+
18+
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
19+
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
20+
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
21+
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
22+
23+
namespace clang::lifetimes::internal {
24+
25+
/// Runs the lifetime checker, which detects use-after-free errors by
26+
/// examining loan expiration points and checking if any live origins hold
27+
/// the expired loan.
28+
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
29+
const LiveOriginsAnalysis &LiveOrigins,
30+
const FactManager &FactMgr, AnalysisDeclContext &ADC,
31+
LifetimeSafetyReporter *Reporter);
32+
33+
} // namespace clang::lifetimes::internal
34+
35+
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
//===- Facts.h - Lifetime Analysis Facts and Fact Manager ------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines Facts, which are atomic lifetime-relevant events (such as
10+
// loan issuance, loan expiration, origin flow, and use), and the FactManager,
11+
// which manages the storage and retrieval of facts for each CFG block.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
15+
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
16+
17+
#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
18+
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
19+
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
20+
#include "clang/Analysis/AnalysisDeclContext.h"
21+
#include "clang/Analysis/CFG.h"
22+
#include "llvm/ADT/SmallVector.h"
23+
#include "llvm/Support/Debug.h"
24+
#include <cstdint>
25+
26+
namespace clang::lifetimes::internal {
27+
28+
using FactID = utils::ID<struct FactTag>;
29+
30+
/// An abstract base class for a single, atomic lifetime-relevant event.
31+
class Fact {
32+
33+
public:
34+
enum class Kind : uint8_t {
35+
/// A new loan is issued from a borrow expression (e.g., &x).
36+
Issue,
37+
/// A loan expires as its underlying storage is freed (e.g., variable goes
38+
/// out of scope).
39+
Expire,
40+
/// An origin is propagated from a source to a destination (e.g., p = q).
41+
/// This can also optionally kill the destination origin before flowing into
42+
/// it. Otherwise, the source's loan set is merged into the destination's
43+
/// loan set.
44+
OriginFlow,
45+
/// An origin is used (eg. appears as l-value expression like DeclRefExpr).
46+
Use,
47+
/// A marker for a specific point in the code, for testing.
48+
TestPoint,
49+
/// An origin that escapes the function scope (e.g., via return).
50+
OriginEscapes,
51+
};
52+
53+
private:
54+
Kind K;
55+
FactID ID;
56+
57+
protected:
58+
Fact(Kind K) : K(K) {}
59+
60+
public:
61+
virtual ~Fact() = default;
62+
Kind getKind() const { return K; }
63+
64+
void setID(FactID ID) { this->ID = ID; }
65+
FactID getID() const { return ID; }
66+
67+
template <typename T> const T *getAs() const {
68+
if (T::classof(this))
69+
return static_cast<const T *>(this);
70+
return nullptr;
71+
}
72+
73+
virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
74+
const OriginManager &) const;
75+
};
76+
77+
/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
78+
/// `Fact`. identified by a lifetime-related event (`Fact`).
79+
///
80+
/// A `ProgramPoint` has "after" semantics: it represents the location
81+
/// immediately after its corresponding `Fact`.
82+
using ProgramPoint = const Fact *;
83+
84+
class IssueFact : public Fact {
85+
LoanID LID;
86+
OriginID OID;
87+
88+
public:
89+
static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }
90+
91+
IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
92+
LoanID getLoanID() const { return LID; }
93+
OriginID getOriginID() const { return OID; }
94+
void dump(llvm::raw_ostream &OS, const LoanManager &LM,
95+
const OriginManager &OM) const override;
96+
};
97+
98+
class ExpireFact : public Fact {
99+
LoanID LID;
100+
SourceLocation ExpiryLoc;
101+
102+
public:
103+
static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
104+
105+
ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
106+
: Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}
107+
108+
LoanID getLoanID() const { return LID; }
109+
SourceLocation getExpiryLoc() const { return ExpiryLoc; }
110+
111+
void dump(llvm::raw_ostream &OS, const LoanManager &LM,
112+
const OriginManager &) const override;
113+
};
114+
115+
class OriginFlowFact : public Fact {
116+
OriginID OIDDest;
117+
OriginID OIDSrc;
118+
// True if the destination origin should be killed (i.e., its current loans
119+
// cleared) before the source origin's loans are flowed into it.
120+
bool KillDest;
121+
122+
public:
123+
static bool classof(const Fact *F) {
124+
return F->getKind() == Kind::OriginFlow;
125+
}
126+
127+
OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
128+
: Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
129+
KillDest(KillDest) {}
130+
131+
OriginID getDestOriginID() const { return OIDDest; }
132+
OriginID getSrcOriginID() const { return OIDSrc; }
133+
bool getKillDest() const { return KillDest; }
134+
135+
void dump(llvm::raw_ostream &OS, const LoanManager &,
136+
const OriginManager &OM) const override;
137+
};
138+
139+
class OriginEscapesFact : public Fact {
140+
OriginID OID;
141+
const Expr *EscapeExpr;
142+
143+
public:
144+
static bool classof(const Fact *F) {
145+
return F->getKind() == Kind::OriginEscapes;
146+
}
147+
148+
OriginEscapesFact(OriginID OID, const Expr *EscapeExpr)
149+
: Fact(Kind::OriginEscapes), OID(OID), EscapeExpr(EscapeExpr) {}
150+
OriginID getEscapedOriginID() const { return OID; }
151+
const Expr *getEscapeExpr() const { return EscapeExpr; };
152+
void dump(llvm::raw_ostream &OS, const LoanManager &,
153+
const OriginManager &OM) const override;
154+
};
155+
156+
class UseFact : public Fact {
157+
const Expr *UseExpr;
158+
OriginID OID;
159+
// True if this use is a write operation (e.g., left-hand side of assignment).
160+
// Write operations are exempted from use-after-free checks.
161+
bool IsWritten = false;
162+
163+
public:
164+
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
165+
166+
UseFact(const Expr *UseExpr, OriginManager &OM)
167+
: Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
168+
169+
OriginID getUsedOrigin() const { return OID; }
170+
const Expr *getUseExpr() const { return UseExpr; }
171+
void markAsWritten() { IsWritten = true; }
172+
bool isWritten() const { return IsWritten; }
173+
174+
void dump(llvm::raw_ostream &OS, const LoanManager &,
175+
const OriginManager &OM) const override;
176+
};
177+
178+
/// A dummy-fact used to mark a specific point in the code for testing.
179+
/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
180+
class TestPointFact : public Fact {
181+
StringRef Annotation;
182+
183+
public:
184+
static bool classof(const Fact *F) { return F->getKind() == Kind::TestPoint; }
185+
186+
explicit TestPointFact(StringRef Annotation)
187+
: Fact(Kind::TestPoint), Annotation(Annotation) {}
188+
189+
StringRef getAnnotation() const { return Annotation; }
190+
191+
void dump(llvm::raw_ostream &OS, const LoanManager &,
192+
const OriginManager &) const override;
193+
};
194+
195+
class FactManager {
196+
public:
197+
void init(const CFG &Cfg) {
198+
assert(BlockToFacts.empty() && "FactManager already initialized");
199+
BlockToFacts.resize(Cfg.getNumBlockIDs());
200+
}
201+
202+
llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
203+
return BlockToFacts[B->getBlockID()];
204+
}
205+
206+
void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
207+
if (!NewFacts.empty())
208+
BlockToFacts[B->getBlockID()].assign(NewFacts.begin(), NewFacts.end());
209+
}
210+
211+
template <typename FactType, typename... Args>
212+
FactType *createFact(Args &&...args) {
213+
void *Mem = FactAllocator.Allocate<FactType>();
214+
FactType *Res = new (Mem) FactType(std::forward<Args>(args)...);
215+
Res->setID(NextFactID++);
216+
return Res;
217+
}
218+
219+
void dump(const CFG &Cfg, AnalysisDeclContext &AC) const;
220+
221+
/// Retrieves program points that were specially marked in the source code
222+
/// for testing.
223+
///
224+
/// The analysis recognizes special function calls of the form
225+
/// `void("__lifetime_test_point_<name>")` as test points. This method returns
226+
/// a map from the annotation string (<name>) to the corresponding
227+
/// `ProgramPoint`. This allows test harnesses to query the analysis state at
228+
/// user-defined locations in the code.
229+
/// \note This is intended for testing only.
230+
llvm::StringMap<ProgramPoint> getTestPoints() const;
231+
/// Retrieves all the facts in the block containing Program Point P.
232+
/// \note This is intended for testing only.
233+
llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;
234+
235+
unsigned getNumFacts() const { return NextFactID.Value; }
236+
237+
LoanManager &getLoanMgr() { return LoanMgr; }
238+
const LoanManager &getLoanMgr() const { return LoanMgr; }
239+
OriginManager &getOriginMgr() { return OriginMgr; }
240+
const OriginManager &getOriginMgr() const { return OriginMgr; }
241+
242+
private:
243+
FactID NextFactID{0};
244+
LoanManager LoanMgr;
245+
OriginManager OriginMgr;
246+
/// Facts for each CFG block, indexed by block ID.
247+
llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts;
248+
llvm::BumpPtrAllocator FactAllocator;
249+
};
250+
} // namespace clang::lifetimes::internal
251+
252+
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H

0 commit comments

Comments
 (0)