Thanks to visit codestin.com
Credit goes to clang.llvm.org

clang 22.0.0git
LifetimeSafety.cpp
Go to the documentation of this file.
1//===- LifetimeSafety.cpp - 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//===----------------------------------------------------------------------===//
9#include "clang/AST/Decl.h"
10#include "clang/AST/Expr.h"
12#include "clang/AST/Type.h"
15#include "clang/Analysis/CFG.h"
17#include "llvm/ADT/FoldingSet.h"
18#include "llvm/ADT/ImmutableMap.h"
19#include "llvm/ADT/ImmutableSet.h"
20#include "llvm/ADT/PointerUnion.h"
21#include "llvm/ADT/SmallBitVector.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/TimeProfiler.h"
25#include <cstdint>
26#include <memory>
27
28namespace clang::lifetimes {
29namespace internal {
30
31/// Represents the storage location being borrowed, e.g., a specific stack
32/// variable.
33/// TODO: Model access paths of other types, e.g., s.field, heap and globals.
34struct AccessPath {
36
38};
39
40/// Information about a single borrow, or "Loan". A loan is created when a
41/// reference or pointer is created.
42struct Loan {
43 /// TODO: Represent opaque loans.
44 /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
45 /// is represented as empty LoanSet
48 /// The expression that creates the loan, e.g., &x.
50
52 : ID(id), Path(path), IssueExpr(IssueExpr) {}
53
54 void dump(llvm::raw_ostream &OS) const {
55 OS << ID << " (Path: ";
56 OS << Path.D->getNameAsString() << ")";
57 }
58};
59
60/// An Origin is a symbolic identifier that represents the set of possible
61/// loans a pointer-like object could hold at any given time.
62/// TODO: Enhance the origin model to handle complex types, pointer
63/// indirection and reborrowing. The plan is to move from a single origin per
64/// variable/expression to a "list of origins" governed by the Type.
65/// For example, the type 'int**' would have two origins.
66/// See discussion:
67/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
68struct Origin {
70 /// A pointer to the AST node that this origin represents. This union
71 /// distinguishes between origins from declarations (variables or parameters)
72 /// and origins from expressions.
73 llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
74
75 Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
76 Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
77
78 const clang::ValueDecl *getDecl() const {
79 return Ptr.dyn_cast<const clang::ValueDecl *>();
80 }
81 const clang::Expr *getExpr() const {
82 return Ptr.dyn_cast<const clang::Expr *>();
83 }
84};
85
86/// Manages the creation, storage and retrieval of loans.
88public:
89 LoanManager() = default;
90
91 Loan &addLoan(AccessPath Path, const Expr *IssueExpr) {
92 AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
93 return AllLoans.back();
94 }
95
96 const Loan &getLoan(LoanID ID) const {
97 assert(ID.Value < AllLoans.size());
98 return AllLoans[ID.Value];
99 }
100 llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }
101
102private:
103 LoanID getNextLoanID() { return NextLoanID++; }
104
105 LoanID NextLoanID{0};
106 /// TODO(opt): Profile and evaluate the usefullness of small buffer
107 /// optimisation.
109};
110
111/// Manages the creation, storage, and retrieval of origins for pointer-like
112/// variables and expressions.
114public:
115 OriginManager() = default;
116
118 AllOrigins.emplace_back(ID, &D);
119 return AllOrigins.back();
120 }
122 AllOrigins.emplace_back(ID, &E);
123 return AllOrigins.back();
124 }
125
126 // TODO: Mark this method as const once we remove the call to getOrCreate.
127 OriginID get(const Expr &E) {
128 auto It = ExprToOriginID.find(&E);
129 if (It != ExprToOriginID.end())
130 return It->second;
131 // If the expression itself has no specific origin, and it's a reference
132 // to a declaration, its origin is that of the declaration it refers to.
133 // For pointer types, where we don't pre-emptively create an origin for the
134 // DeclRefExpr itself.
135 if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
136 return get(*DRE->getDecl());
137 // TODO: This should be an assert(It != ExprToOriginID.end()). The current
138 // implementation falls back to getOrCreate to avoid crashing on
139 // yet-unhandled pointer expressions, creating an empty origin for them.
140 return getOrCreate(E);
141 }
142
144 auto It = DeclToOriginID.find(&D);
145 // TODO: This should be an assert(It != DeclToOriginID.end()). The current
146 // implementation falls back to getOrCreate to avoid crashing on
147 // yet-unhandled pointer expressions, creating an empty origin for them.
148 if (It == DeclToOriginID.end())
149 return getOrCreate(D);
150
151 return It->second;
152 }
153
155 auto It = ExprToOriginID.find(&E);
156 if (It != ExprToOriginID.end())
157 return It->second;
158
159 OriginID NewID = getNextOriginID();
160 addOrigin(NewID, E);
161 ExprToOriginID[&E] = NewID;
162 return NewID;
163 }
164
165 const Origin &getOrigin(OriginID ID) const {
166 assert(ID.Value < AllOrigins.size());
167 return AllOrigins[ID.Value];
168 }
169
170 llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
171
173 auto It = DeclToOriginID.find(&D);
174 if (It != DeclToOriginID.end())
175 return It->second;
176 OriginID NewID = getNextOriginID();
177 addOrigin(NewID, D);
178 DeclToOriginID[&D] = NewID;
179 return NewID;
180 }
181
182 void dump(OriginID OID, llvm::raw_ostream &OS) const {
183 OS << OID << " (";
184 Origin O = getOrigin(OID);
185 if (const ValueDecl *VD = O.getDecl())
186 OS << "Decl: " << VD->getNameAsString();
187 else if (const Expr *E = O.getExpr())
188 OS << "Expr: " << E->getStmtClassName();
189 else
190 OS << "Unknown";
191 OS << ")";
192 }
193
194private:
195 OriginID getNextOriginID() { return NextOriginID++; }
196
197 OriginID NextOriginID{0};
198 /// TODO(opt): Profile and evaluate the usefullness of small buffer
199 /// optimisation.
200 llvm::SmallVector<Origin> AllOrigins;
201 llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
202 llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
203};
204
205/// An abstract base class for a single, atomic lifetime-relevant event.
206class Fact {
207
208public:
209 enum class Kind : uint8_t {
210 /// A new loan is issued from a borrow expression (e.g., &x).
212 /// A loan expires as its underlying storage is freed (e.g., variable goes
213 /// out of scope).
215 /// An origin is propagated from a source to a destination (e.g., p = q).
217 /// An origin escapes the function by flowing into the return value.
219 /// An origin is used (eg. dereferencing a pointer).
221 /// A marker for a specific point in the code, for testing.
223 };
224
225private:
226 Kind K;
227
228protected:
229 Fact(Kind K) : K(K) {}
230
231public:
232 virtual ~Fact() = default;
233 Kind getKind() const { return K; }
234
235 template <typename T> const T *getAs() const {
236 if (T::classof(this))
237 return static_cast<const T *>(this);
238 return nullptr;
239 }
240
241 virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
242 const OriginManager &) const {
243 OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
244 }
245};
246
247class IssueFact : public Fact {
248 LoanID LID;
249 OriginID OID;
250
251public:
252 static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }
253
254 IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
255 LoanID getLoanID() const { return LID; }
256 OriginID getOriginID() const { return OID; }
257 void dump(llvm::raw_ostream &OS, const LoanManager &LM,
258 const OriginManager &OM) const override {
259 OS << "Issue (";
260 LM.getLoan(getLoanID()).dump(OS);
261 OS << ", ToOrigin: ";
262 OM.dump(getOriginID(), OS);
263 OS << ")\n";
264 }
265};
266
267class ExpireFact : public Fact {
268 LoanID LID;
269 SourceLocation ExpiryLoc;
270
271public:
272 static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
273
275 : Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}
276
277 LoanID getLoanID() const { return LID; }
278 SourceLocation getExpiryLoc() const { return ExpiryLoc; }
279
280 void dump(llvm::raw_ostream &OS, const LoanManager &LM,
281 const OriginManager &) const override {
282 OS << "Expire (";
283 LM.getLoan(getLoanID()).dump(OS);
284 OS << ")\n";
285 }
286};
287
288class AssignOriginFact : public Fact {
289 OriginID OIDDest;
290 OriginID OIDSrc;
291
292public:
293 static bool classof(const Fact *F) {
294 return F->getKind() == Kind::AssignOrigin;
295 }
296
298 : Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
299 OriginID getDestOriginID() const { return OIDDest; }
300 OriginID getSrcOriginID() const { return OIDSrc; }
301 void dump(llvm::raw_ostream &OS, const LoanManager &,
302 const OriginManager &OM) const override {
303 OS << "AssignOrigin (Dest: ";
304 OM.dump(getDestOriginID(), OS);
305 OS << ", Src: ";
306 OM.dump(getSrcOriginID(), OS);
307 OS << ")\n";
308 }
309};
310
311class ReturnOfOriginFact : public Fact {
312 OriginID OID;
313
314public:
315 static bool classof(const Fact *F) {
316 return F->getKind() == Kind::ReturnOfOrigin;
317 }
318
320 OriginID getReturnedOriginID() const { return OID; }
321 void dump(llvm::raw_ostream &OS, const LoanManager &,
322 const OriginManager &OM) const override {
323 OS << "ReturnOfOrigin (";
324 OM.dump(getReturnedOriginID(), OS);
325 OS << ")\n";
326 }
327};
328
329class UseFact : public Fact {
330 const Expr *UseExpr;
331 // True if this use is a write operation (e.g., left-hand side of assignment).
332 // Write operations are exempted from use-after-free checks.
333 bool IsWritten = false;
334
335public:
336 static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
337
338 UseFact(const Expr *UseExpr) : Fact(Kind::Use), UseExpr(UseExpr) {}
339
341 // TODO: Remove const cast and make OriginManager::get as const.
342 return const_cast<OriginManager &>(OM).get(*UseExpr);
343 }
344 const Expr *getUseExpr() const { return UseExpr; }
345 void markAsWritten() { IsWritten = true; }
346 bool isWritten() const { return IsWritten; }
347
348 void dump(llvm::raw_ostream &OS, const LoanManager &,
349 const OriginManager &OM) const override {
350 OS << "Use (";
351 OM.dump(getUsedOrigin(OM), OS);
352 OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
353 }
354};
355
356/// A dummy-fact used to mark a specific point in the code for testing.
357/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
358class TestPointFact : public Fact {
359 StringRef Annotation;
360
361public:
362 static bool classof(const Fact *F) { return F->getKind() == Kind::TestPoint; }
363
364 explicit TestPointFact(StringRef Annotation)
365 : Fact(Kind::TestPoint), Annotation(Annotation) {}
366
367 StringRef getAnnotation() const { return Annotation; }
368
369 void dump(llvm::raw_ostream &OS, const LoanManager &,
370 const OriginManager &) const override {
371 OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
372 }
373};
374
376public:
378 auto It = BlockToFactsMap.find(B);
379 if (It != BlockToFactsMap.end())
380 return It->second;
381 return {};
382 }
383
385 if (!NewFacts.empty())
386 BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
387 }
388
389 template <typename FactType, typename... Args>
390 FactType *createFact(Args &&...args) {
391 void *Mem = FactAllocator.Allocate<FactType>();
392 return new (Mem) FactType(std::forward<Args>(args)...);
393 }
394
395 void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
396 llvm::dbgs() << "==========================================\n";
397 llvm::dbgs() << " Lifetime Analysis Facts:\n";
398 llvm::dbgs() << "==========================================\n";
399 if (const Decl *D = AC.getDecl())
400 if (const auto *ND = dyn_cast<NamedDecl>(D))
401 llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
402 // Print blocks in the order as they appear in code for a stable ordering.
403 for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
404 llvm::dbgs() << " Block B" << B->getBlockID() << ":\n";
405 auto It = BlockToFactsMap.find(B);
406 if (It != BlockToFactsMap.end()) {
407 for (const Fact *F : It->second) {
408 llvm::dbgs() << " ";
409 F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
410 }
411 }
412 llvm::dbgs() << " End of Block\n";
413 }
414 }
415
416 LoanManager &getLoanMgr() { return LoanMgr; }
417 OriginManager &getOriginMgr() { return OriginMgr; }
418
419private:
420 LoanManager LoanMgr;
421 OriginManager OriginMgr;
422 llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
423 BlockToFactsMap;
424 llvm::BumpPtrAllocator FactAllocator;
425};
426
427class FactGenerator : public ConstStmtVisitor<FactGenerator> {
429
430public:
432 : FactMgr(FactMgr), AC(AC) {}
433
434 void run() {
435 llvm::TimeTraceScope TimeProfile("FactGenerator");
436 // Iterate through the CFG blocks in reverse post-order to ensure that
437 // initializations and destructions are processed in the correct sequence.
438 for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
439 CurrentBlockFacts.clear();
440 for (unsigned I = 0; I < Block->size(); ++I) {
441 const CFGElement &Element = Block->Elements[I];
442 if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
443 Visit(CS->getStmt());
444 else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
445 Element.getAs<CFGAutomaticObjDtor>())
446 handleDestructor(*DtorOpt);
447 }
448 FactMgr.addBlockFacts(Block, CurrentBlockFacts);
449 }
450 }
451
452 void VisitDeclStmt(const DeclStmt *DS) {
453 for (const Decl *D : DS->decls())
454 if (const auto *VD = dyn_cast<VarDecl>(D))
455 if (hasOrigin(VD))
456 if (const Expr *InitExpr = VD->getInit())
457 addAssignOriginFact(*VD, *InitExpr);
458 }
459
460 void VisitDeclRefExpr(const DeclRefExpr *DRE) {
461 handleUse(DRE);
462 // For non-pointer/non-view types, a reference to the variable's storage
463 // is a borrow. We create a loan for it.
464 // For pointer/view types, we stick to the existing model for now and do
465 // not create an extra origin for the l-value expression itself.
466
467 // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
468 // not sufficient to model the different levels of indirection. The current
469 // single-origin model cannot distinguish between a loan to the variable's
470 // storage and a loan to what it points to. A multi-origin model would be
471 // required for this.
472 if (!isPointerType(DRE->getType())) {
473 if (const Loan *L = createLoan(DRE)) {
474 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
475 CurrentBlockFacts.push_back(
476 FactMgr.createFact<IssueFact>(L->ID, ExprOID));
477 }
478 }
479 }
480
482 if (isGslPointerType(CCE->getType())) {
483 handleGSLPointerConstruction(CCE);
484 return;
485 }
486 }
487
489 // Specifically for conversion operators,
490 // like `std::string_view p = std::string{};`
491 if (isGslPointerType(MCE->getType()) &&
493 // The argument is the implicit object itself.
494 handleFunctionCall(MCE, MCE->getMethodDecl(),
495 {MCE->getImplicitObjectArgument()});
496 }
497 // FIXME: A more general VisitCallExpr could also be used here.
498 }
499
501 /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
502 /// pointers can use the same type of loan.
503 FactMgr.getOriginMgr().getOrCreate(*N);
504 }
505
507 if (!hasOrigin(ICE))
508 return;
509 // An ImplicitCastExpr node itself gets an origin, which flows from the
510 // origin of its sub-expression (after stripping its own parens/casts).
511 addAssignOriginFact(*ICE, *ICE->getSubExpr());
512 }
513
515 if (UO->getOpcode() == UO_AddrOf) {
516 const Expr *SubExpr = UO->getSubExpr();
517 // Taking address of a pointer-type expression is not yet supported and
518 // will be supported in multi-origin model.
519 if (isPointerType(SubExpr->getType()))
520 return;
521 // The origin of an address-of expression (e.g., &x) is the origin of
522 // its sub-expression (x). This fact will cause the dataflow analysis
523 // to propagate any loans held by the sub-expression's origin to the
524 // origin of this UnaryOperator expression.
525 addAssignOriginFact(*UO, *SubExpr);
526 }
527 }
528
529 void VisitReturnStmt(const ReturnStmt *RS) {
530 if (const Expr *RetExpr = RS->getRetValue()) {
531 if (hasOrigin(RetExpr)) {
532 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
533 CurrentBlockFacts.push_back(
534 FactMgr.createFact<ReturnOfOriginFact>(OID));
535 }
536 }
537 }
538
540 if (BO->isAssignmentOp())
541 handleAssignment(BO->getLHS(), BO->getRHS());
542 }
543
545 if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2)
546 handleAssignment(OCE->getArg(0), OCE->getArg(1));
547 }
548
550 // Check if this is a test point marker. If so, we are done with this
551 // expression.
552 if (handleTestPoint(FCE))
553 return;
554 if (isGslPointerType(FCE->getType()))
555 addAssignOriginFact(*FCE, *FCE->getSubExpr());
556 }
557
559 if (!hasOrigin(ILE))
560 return;
561 // For list initialization with a single element, like `View{...}`, the
562 // origin of the list itself is the origin of its single element.
563 if (ILE->getNumInits() == 1)
564 addAssignOriginFact(*ILE, *ILE->getInit(0));
565 }
566
568 if (!hasOrigin(MTE))
569 return;
570 // A temporary object's origin is the same as the origin of the
571 // expression that initializes it.
572 addAssignOriginFact(*MTE, *MTE->getSubExpr());
573 }
574
576 /// TODO: Also handle trivial destructors (e.g., for `int`
577 /// variables) which will never have a CFGAutomaticObjDtor node.
578 /// TODO: Handle loans to temporaries.
579 /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
580 /// lifetime ends.
581 const VarDecl *DestructedVD = DtorOpt.getVarDecl();
582 if (!DestructedVD)
583 return;
584 // Iterate through all loans to see if any expire.
585 /// TODO(opt): Do better than a linear search to find loans associated with
586 /// 'DestructedVD'.
587 for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
588 const AccessPath &LoanPath = L.Path;
589 // Check if the loan is for a stack variable and if that variable
590 // is the one being destructed.
591 if (LoanPath.D == DestructedVD)
592 CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
593 L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
594 }
595 }
596
597private:
598 static bool isGslPointerType(QualType QT) {
599 if (const auto *RD = QT->getAsCXXRecordDecl()) {
600 // We need to check the template definition for specializations.
601 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
602 return CTSD->getSpecializedTemplate()
603 ->getTemplatedDecl()
604 ->hasAttr<PointerAttr>();
605 return RD->hasAttr<PointerAttr>();
606 }
607 return false;
608 }
609
610 static bool isPointerType(QualType QT) {
611 return QT->isPointerOrReferenceType() || isGslPointerType(QT);
612 }
613 // Check if a type has an origin.
614 static bool hasOrigin(const Expr *E) {
615 return E->isGLValue() || isPointerType(E->getType());
616 }
617
618 static bool hasOrigin(const VarDecl *VD) {
619 return isPointerType(VD->getType());
620 }
621
622 void handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
623 assert(isGslPointerType(CCE->getType()));
624 if (CCE->getNumArgs() != 1)
625 return;
626 if (hasOrigin(CCE->getArg(0)))
627 addAssignOriginFact(*CCE, *CCE->getArg(0));
628 else
629 // This could be a new borrow.
630 handleFunctionCall(CCE, CCE->getConstructor(),
631 {CCE->getArgs(), CCE->getNumArgs()});
632 }
633
634 /// Checks if a call-like expression creates a borrow by passing a value to a
635 /// reference parameter, creating an IssueFact if it does.
636 void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
637 ArrayRef<const Expr *> Args) {
638 if (!FD)
639 return;
640 // TODO: Handle more than one arguments.
641 for (unsigned I = 0; I <= 0 /*Args.size()*/; ++I) {
642 const Expr *ArgExpr = Args[I];
643
644 // Propagate origins for CXX this.
645 if (FD->isCXXClassMember() && I == 0) {
646 addAssignOriginFact(*Call, *ArgExpr);
647 continue;
648 }
649 // The parameter is a pointer, reference, or gsl::Pointer.
650 // This is a borrow. We propagate the origin from the argument expression
651 // at the call site to the parameter declaration in the callee.
652 if (hasOrigin(ArgExpr))
653 addAssignOriginFact(*Call, *ArgExpr);
654 }
655 }
656
657 /// Creates a loan for the storage path of a given declaration reference.
658 /// This function should be called whenever a DeclRefExpr represents a borrow.
659 /// \param DRE The declaration reference expression that initiates the borrow.
660 /// \return The new Loan on success, nullptr otherwise.
661 const Loan *createLoan(const DeclRefExpr *DRE) {
662 if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
663 AccessPath Path(VD);
664 // The loan is created at the location of the DeclRefExpr.
665 return &FactMgr.getLoanMgr().addLoan(Path, DRE);
666 }
667 return nullptr;
668 }
669
670 template <typename Destination, typename Source>
671 void addAssignOriginFact(const Destination &D, const Source &S) {
672 OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
673 OriginID SrcOID = FactMgr.getOriginMgr().get(S);
674 CurrentBlockFacts.push_back(
675 FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
676 }
677
678 /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
679 /// If so, creates a `TestPointFact` and returns true.
680 bool handleTestPoint(const CXXFunctionalCastExpr *FCE) {
681 if (!FCE->getType()->isVoidType())
682 return false;
683
684 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
685 if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
686 llvm::StringRef LiteralValue = SL->getString();
687 const std::string Prefix = "__lifetime_test_point_";
688
689 if (LiteralValue.starts_with(Prefix)) {
690 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
691 CurrentBlockFacts.push_back(
692 FactMgr.createFact<TestPointFact>(Annotation));
693 return true;
694 }
695 }
696 return false;
697 }
698
699 void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
700 if (!hasOrigin(LHSExpr))
701 return;
702 // Find the underlying variable declaration for the left-hand side.
703 if (const auto *DRE_LHS =
704 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
705 markUseAsWrite(DRE_LHS);
706 if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
707 // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`.
708 // LHS must be a pointer/reference type that can be an origin. RHS must
709 // also represent an origin (either another pointer/ref or an
710 // address-of).
711 addAssignOriginFact(*VD_LHS, *RHSExpr);
712 }
713 }
714
715 // A DeclRefExpr will be treated as a use of the referenced decl. It will be
716 // checked for use-after-free unless it is later marked as being written to
717 // (e.g. on the left-hand side of an assignment).
718 void handleUse(const DeclRefExpr *DRE) {
719 if (isPointerType(DRE->getType())) {
720 UseFact *UF = FactMgr.createFact<UseFact>(DRE);
721 CurrentBlockFacts.push_back(UF);
722 assert(!UseFacts.contains(DRE));
723 UseFacts[DRE] = UF;
724 }
725 }
726
727 void markUseAsWrite(const DeclRefExpr *DRE) {
728 if (!isPointerType(DRE->getType()))
729 return;
730 assert(UseFacts.contains(DRE));
731 UseFacts[DRE]->markAsWritten();
732 }
733
734 FactManager &FactMgr;
735 AnalysisDeclContext &AC;
736 llvm::SmallVector<Fact *> CurrentBlockFacts;
737 // To distinguish between reads and writes for use-after-free checks, this map
738 // stores the `UseFact` for each `DeclRefExpr`. We initially identify all
739 // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
740 // corresponding to the left-hand side is updated to be a "write", thereby
741 // exempting it from the check.
742 llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
743};
744
745// ========================================================================= //
746// Generic Dataflow Analysis
747// ========================================================================= //
748
749enum class Direction { Forward, Backward };
750
751/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
752/// `Fact`. identified by a lifetime-related event (`Fact`).
753///
754/// A `ProgramPoint` has "after" semantics: it represents the location
755/// immediately after its corresponding `Fact`.
756using ProgramPoint = const Fact *;
757
758/// A generic, policy-based driver for dataflow analyses. It combines
759/// the dataflow runner and the transferer logic into a single class hierarchy.
760///
761/// The derived class is expected to provide:
762/// - A `Lattice` type.
763/// - `StringRef getAnalysisName() const`
764/// - `Lattice getInitialState();` The initial state of the analysis.
765/// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths.
766/// - `Lattice transfer(Lattice, const FactType&);` Defines how a single
767/// lifetime-relevant `Fact` transforms the lattice state. Only overloads
768/// for facts relevant to the analysis need to be implemented.
769///
770/// \tparam Derived The CRTP derived class that implements the specific
771/// analysis.
772/// \tparam LatticeType The dataflow lattice used by the analysis.
773/// \tparam Dir The direction of the analysis (Forward or Backward).
774/// TODO: Maybe use the dataflow framework! The framework might need changes
775/// to support the current comparison done at block-entry.
776template <typename Derived, typename LatticeType, Direction Dir>
778public:
779 using Lattice = LatticeType;
781
782private:
783 const CFG &Cfg;
785
786 /// The dataflow state before a basic block is processed.
787 llvm::DenseMap<const CFGBlock *, Lattice> InStates;
788 /// The dataflow state after a basic block is processed.
789 llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
790 /// The dataflow state at a Program Point.
791 /// In a forward analysis, this is the state after the Fact at that point has
792 /// been applied, while in a backward analysis, it is the state before.
793 llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
794
795 static constexpr bool isForward() { return Dir == Direction::Forward; }
796
797protected:
799
801 FactManager &F)
802 : Cfg(C), AC(AC), AllFacts(F) {}
803
804public:
805 void run() {
806 Derived &D = static_cast<Derived &>(*this);
807 llvm::TimeTraceScope Time(D.getAnalysisName());
808
809 using Worklist =
810 std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist,
812 Worklist W(Cfg, AC);
813
814 const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
815 InStates[Start] = D.getInitialState();
816 W.enqueueBlock(Start);
817
818 llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
819
820 while (const CFGBlock *B = W.dequeue()) {
821 Lattice StateIn = getInState(B);
822 Lattice StateOut = transferBlock(B, StateIn);
823 OutStates[B] = StateOut;
824 Visited.set(B->getBlockID());
825 for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
826 if (!AdjacentB)
827 continue;
828 Lattice OldInState = getInState(AdjacentB);
829 Lattice NewInState = D.join(OldInState, StateOut);
830 // Enqueue the adjacent block if its in-state has changed or if we have
831 // never visited it.
832 if (!Visited.test(AdjacentB->getBlockID()) ||
833 NewInState != OldInState) {
834 InStates[AdjacentB] = NewInState;
835 W.enqueueBlock(AdjacentB);
836 }
837 }
838 }
839 }
840
841protected:
842 Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
843
844 Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
845
846 Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
847
848 void dump() const {
849 const Derived *D = static_cast<const Derived *>(this);
850 llvm::dbgs() << "==========================================\n";
851 llvm::dbgs() << D->getAnalysisName() << " results:\n";
852 llvm::dbgs() << "==========================================\n";
853 const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
854 getOutState(&B).dump(llvm::dbgs());
855 }
856
857private:
858 /// Computes the state at one end of a block by applying all its facts
859 /// sequentially to a given state from the other end.
860 Lattice transferBlock(const CFGBlock *Block, Lattice State) {
861 auto Facts = AllFacts.getFacts(Block);
862 if constexpr (isForward()) {
863 for (const Fact *F : Facts) {
864 State = transferFact(State, F);
865 PerPointStates[F] = State;
866 }
867 } else {
868 for (const Fact *F : llvm::reverse(Facts)) {
869 // In backward analysis, capture the state before applying the fact.
870 PerPointStates[F] = State;
871 State = transferFact(State, F);
872 }
873 }
874 return State;
875 }
876
877 Lattice transferFact(Lattice In, const Fact *F) {
878 assert(F);
879 Derived *D = static_cast<Derived *>(this);
880 switch (F->getKind()) {
882 return D->transfer(In, *F->getAs<IssueFact>());
884 return D->transfer(In, *F->getAs<ExpireFact>());
886 return D->transfer(In, *F->getAs<AssignOriginFact>());
888 return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
889 case Fact::Kind::Use:
890 return D->transfer(In, *F->getAs<UseFact>());
892 return D->transfer(In, *F->getAs<TestPointFact>());
893 }
894 llvm_unreachable("Unknown fact kind");
895 }
896
897public:
898 Lattice transfer(Lattice In, const IssueFact &) { return In; }
899 Lattice transfer(Lattice In, const ExpireFact &) { return In; }
900 Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
901 Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
902 Lattice transfer(Lattice In, const UseFact &) { return In; }
903 Lattice transfer(Lattice In, const TestPointFact &) { return In; }
904};
905
906namespace utils {
907
908/// Computes the union of two ImmutableSets.
909template <typename T>
910static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
911 llvm::ImmutableSet<T> B,
912 typename llvm::ImmutableSet<T>::Factory &F) {
913 if (A == B)
914 return A;
915 if (A.getHeight() < B.getHeight())
916 std::swap(A, B);
917 for (const T &E : B)
918 if (!A.contains(E))
919 A = F.add(A, E);
920 return A;
921}
922
923/// Checks if set A is a subset of set B.
924template <typename T>
925static bool isSubsetOf(const llvm::ImmutableSet<T> &A,
926 const llvm::ImmutableSet<T> &B) {
927 // Empty set is a subset of all sets.
928 if (A.isEmpty())
929 return true;
930
931 for (const T &Elem : A)
932 if (!B.contains(Elem))
933 return false;
934 return true;
935}
936
937/// Computes the key-wise union of two ImmutableMaps.
938// TODO(opt): This key-wise join is a performance bottleneck. A more
939// efficient merge could be implemented using a Patricia Trie or HAMT
940// instead of the current AVL-tree-based ImmutableMap.
941template <typename K, typename V, typename Joiner>
942static llvm::ImmutableMap<K, V>
943join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
944 typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
945 if (A.getHeight() < B.getHeight())
946 std::swap(A, B);
947
948 // For each element in B, join it with the corresponding element in A
949 // (or with an empty value if it doesn't exist in A).
950 for (const auto &Entry : B) {
951 const K &Key = Entry.first;
952 const V &ValB = Entry.second;
953 const V *ValA = A.lookup(Key);
954 if (!ValA)
955 A = F.add(A, Key, ValB);
956 else if (*ValA != ValB)
957 A = F.add(A, Key, JoinValues(*ValA, ValB));
958 }
959 return A;
960}
961} // namespace utils
962
963// ========================================================================= //
964// Loan Propagation Analysis
965// ========================================================================= //
966
967using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
968using ExpiredLoanMap = llvm::ImmutableMap<LoanID, const ExpireFact *>;
969
970/// An object to hold the factories for immutable collections, ensuring
971/// that all created states share the same underlying memory management.
973 OriginLoanMap::Factory OriginMapFactory;
974 LoanSet::Factory LoanSetFactory;
975 ExpiredLoanMap::Factory ExpiredLoanMapFactory;
976};
977
978/// Represents the dataflow lattice for loan propagation.
979///
980/// This lattice tracks which loans each origin may hold at a given program
981/// point.The lattice has a finite height: An origin's loan set is bounded by
982/// the total number of loans in the function.
983/// TODO(opt): To reduce the lattice size, propagate origins of declarations,
984/// not expressions, because expressions are not visible across blocks.
986 /// The map from an origin to the set of loans it contains.
988
991
993 return Origins == Other.Origins;
994 }
996 return !(*this == Other);
997 }
998
999 void dump(llvm::raw_ostream &OS) const {
1000 OS << "LoanPropagationLattice State:\n";
1001 if (Origins.isEmpty())
1002 OS << " <empty>\n";
1003 for (const auto &Entry : Origins) {
1004 if (Entry.second.isEmpty())
1005 OS << " Origin " << Entry.first << " contains no loans\n";
1006 for (const LoanID &LID : Entry.second)
1007 OS << " Origin " << Entry.first << " contains Loan " << LID << "\n";
1008 }
1009 }
1010};
1011
1012/// The analysis that tracks which loans belong to which origins.
1014 : public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
1015 Direction::Forward> {
1016 OriginLoanMap::Factory &OriginLoanMapFactory;
1017 LoanSet::Factory &LoanSetFactory;
1018
1019public:
1021 LifetimeFactory &LFactory)
1022 : DataflowAnalysis(C, AC, F),
1023 OriginLoanMapFactory(LFactory.OriginMapFactory),
1024 LoanSetFactory(LFactory.LoanSetFactory) {}
1025
1026 using Base::transfer;
1027
1028 StringRef getAnalysisName() const { return "LoanPropagation"; }
1029
1031
1032 /// Merges two lattices by taking the union of loans for each origin.
1033 // TODO(opt): Keep the state small by removing origins which become dead.
1035 OriginLoanMap JoinedOrigins =
1036 utils::join(A.Origins, B.Origins, OriginLoanMapFactory,
1037 [&](LoanSet S1, LoanSet S2) {
1038 return utils::join(S1, S2, LoanSetFactory);
1039 });
1040 return Lattice(JoinedOrigins);
1041 }
1042
1043 /// A new loan is issued to the origin. Old loans are erased.
1045 OriginID OID = F.getOriginID();
1046 LoanID LID = F.getLoanID();
1047 return LoanPropagationLattice(OriginLoanMapFactory.add(
1048 In.Origins, OID,
1049 LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
1050 }
1051
1052 /// The destination origin's loan set is replaced by the source's.
1053 /// This implicitly "resets" the old loans of the destination.
1055 OriginID DestOID = F.getDestOriginID();
1056 OriginID SrcOID = F.getSrcOriginID();
1057 LoanSet SrcLoans = getLoans(In, SrcOID);
1059 OriginLoanMapFactory.add(In.Origins, DestOID, SrcLoans));
1060 }
1061
1063 return getLoans(getState(P), OID);
1064 }
1065
1066private:
1068 if (auto *Loans = L.Origins.lookup(OID))
1069 return *Loans;
1070 return LoanSetFactory.getEmptySet();
1071 }
1072};
1073
1074// ========================================================================= //
1075// Expired Loans Analysis
1076// ========================================================================= //
1077
1078/// The dataflow lattice for tracking the set of expired loans.
1080 /// Map from an expired `LoanID` to the `ExpireFact` that made it expire.
1082
1085
1086 bool operator==(const ExpiredLattice &Other) const {
1087 return Expired == Other.Expired;
1088 }
1089 bool operator!=(const ExpiredLattice &Other) const {
1090 return !(*this == Other);
1091 }
1092
1093 void dump(llvm::raw_ostream &OS) const {
1094 OS << "ExpiredLattice State:\n";
1095 if (Expired.isEmpty())
1096 OS << " <empty>\n";
1097 for (const auto &[ID, _] : Expired)
1098 OS << " Loan " << ID << " is expired\n";
1099 }
1100};
1101
1102/// The analysis that tracks which loans have expired.
1104 : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
1105 Direction::Forward> {
1106
1107 ExpiredLoanMap::Factory &Factory;
1108
1109public:
1111 LifetimeFactory &Factory)
1112 : DataflowAnalysis(C, AC, F), Factory(Factory.ExpiredLoanMapFactory) {}
1113
1114 using Base::transfer;
1115
1116 StringRef getAnalysisName() const { return "ExpiredLoans"; }
1117
1118 Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
1119
1120 /// Merges two lattices by taking the union of the two expired loans.
1122 return Lattice(
1123 utils::join(L1.Expired, L2.Expired, Factory,
1124 // Take the last expiry fact to make this hermetic.
1125 [](const ExpireFact *F1, const ExpireFact *F2) {
1126 return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
1127 }));
1128 }
1129
1131 return Lattice(Factory.add(In.Expired, F.getLoanID(), &F));
1132 }
1133
1134 // Removes the loan from the set of expired loans.
1135 //
1136 // When a loan is re-issued (e.g., in a loop), it is no longer considered
1137 // expired. A loan can be in the expired set at the point of issue due to
1138 // the dataflow state from a previous loop iteration being propagated along
1139 // a backedge in the CFG.
1140 //
1141 // Note: This has a subtle false-negative though where a loan from previous
1142 // iteration is not overwritten by a reissue. This needs careful tracking
1143 // of loans "across iterations" which can be considered for future
1144 // enhancements.
1145 //
1146 // void foo(int safe) {
1147 // int* p = &safe;
1148 // int* q = &safe;
1149 // while (condition()) {
1150 // int x = 1;
1151 // p = &x; // A loan to 'x' is issued to 'p' in every iteration.
1152 // if (condition()) {
1153 // q = p;
1154 // }
1155 // (void)*p; // OK — 'p' points to 'x' from new iteration.
1156 // (void)*q; // UaF - 'q' still points to 'x' from previous iteration
1157 // // which is now destroyed.
1158 // }
1159 // }
1161 return Lattice(Factory.remove(In.Expired, F.getLoanID()));
1162 }
1163
1165};
1166
1167// ========================================================================= //
1168// Lifetime checker and Error reporter
1169// ========================================================================= //
1170
1171/// Struct to store the complete context for a potential lifetime violation.
1173 SourceLocation ExpiryLoc; // Where the loan expired.
1174 const Expr *UseExpr; // Where the origin holding this loan was used.
1176};
1177
1179private:
1180 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
1181 LoanPropagationAnalysis &LoanPropagation;
1182 ExpiredLoansAnalysis &ExpiredLoans;
1183 FactManager &FactMgr;
1185 LifetimeSafetyReporter *Reporter;
1186
1187public:
1190 LifetimeSafetyReporter *Reporter)
1191 : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
1192 Reporter(Reporter) {}
1193
1194 void run() {
1195 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
1196 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
1197 for (const Fact *F : FactMgr.getFacts(B))
1198 if (const auto *UF = F->getAs<UseFact>())
1199 checkUse(UF);
1201 }
1202
1203 /// Checks for use-after-free errors for a given use of an Origin.
1204 ///
1205 /// This method is called for each 'UseFact' identified in the control flow
1206 /// graph. It determines if the loans held by the used origin have expired
1207 /// at the point of use.
1208 void checkUse(const UseFact *UF) {
1209 if (UF->isWritten())
1210 return;
1211 OriginID O = UF->getUsedOrigin(FactMgr.getOriginMgr());
1212
1213 // Get the set of loans that the origin might hold at this program point.
1214 LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
1215
1216 // Get the set of all loans that have expired at this program point.
1217 ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
1218
1219 // If the pointer holds no loans or no loans have expired, there's nothing
1220 // to check.
1221 if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
1222 return;
1223
1224 // Identify loans that which have expired but are held by the pointer. Using
1225 // them is a use-after-free.
1226 llvm::SmallVector<LoanID> DefaultedLoans;
1227 // A definite UaF error occurs if all loans the origin might hold have
1228 // expired.
1229 bool IsDefiniteError = true;
1230 for (LoanID L : HeldLoans) {
1231 if (AllExpiredLoans.contains(L))
1232 DefaultedLoans.push_back(L);
1233 else
1234 // If at least one loan is not expired, this use is not a definite UaF.
1235 IsDefiniteError = false;
1236 }
1237 // If there are no defaulted loans, the use is safe.
1238 if (DefaultedLoans.empty())
1239 return;
1240
1241 // Determine the confidence level of the error (definite or maybe).
1242 Confidence CurrentConfidence =
1243 IsDefiniteError ? Confidence::Definite : Confidence::Maybe;
1244
1245 // For each expired loan, create a pending warning.
1246 for (LoanID DefaultedLoan : DefaultedLoans) {
1247 // If we already have a warning for this loan with a higher or equal
1248 // confidence, skip this one.
1249 if (FinalWarningsMap.count(DefaultedLoan) &&
1250 CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
1251 continue;
1252
1253 auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
1254 assert(EF && "Could not find ExpireFact for an expired loan.");
1255
1256 FinalWarningsMap[DefaultedLoan] = {/*ExpiryLoc=*/(*EF)->getExpiryLoc(),
1257 /*UseExpr=*/UF->getUseExpr(),
1258 /*ConfidenceLevel=*/CurrentConfidence};
1259 }
1260 }
1261
1263 if (!Reporter)
1264 return;
1265 for (const auto &[LID, Warning] : FinalWarningsMap) {
1266 const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
1267 const Expr *IssueExpr = L.IssueExpr;
1268 Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr,
1269 Warning.ExpiryLoc, Warning.ConfidenceLevel);
1270 }
1271 }
1272};
1273
1274// ========================================================================= //
1275// LifetimeSafetyAnalysis Class Implementation
1276// ========================================================================= //
1277
1278// We need this here for unique_ptr with forward declared class.
1280
1282 LifetimeSafetyReporter *Reporter)
1283 : AC(AC), Reporter(Reporter), Factory(std::make_unique<LifetimeFactory>()),
1284 FactMgr(std::make_unique<FactManager>()) {}
1285
1287 llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
1288
1289 const CFG &Cfg = *AC.getCFG();
1290 DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
1291 /*ShowColors=*/true));
1292
1293 FactGenerator FactGen(*FactMgr, AC);
1294 FactGen.run();
1295 DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
1296
1297 /// TODO(opt): Consider optimizing individual blocks before running the
1298 /// dataflow analysis.
1299 /// 1. Expression Origins: These are assigned once and read at most once,
1300 /// forming simple chains. These chains can be compressed into a single
1301 /// assignment.
1302 /// 2. Block-Local Loans: Origins of expressions are never read by other
1303 /// blocks; only Decls are visible. Therefore, loans in a block that
1304 /// never reach an Origin associated with a Decl can be safely dropped by
1305 /// the analysis.
1306 /// 3. Collapse ExpireFacts belonging to same source location into a single
1307 /// Fact.
1308 LoanPropagation =
1309 std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
1310 LoanPropagation->run();
1311
1312 ExpiredLoans =
1313 std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
1314 ExpiredLoans->run();
1315
1316 LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
1317 Reporter);
1318 Checker.run();
1319}
1320
1322 ProgramPoint PP) const {
1323 assert(LoanPropagation && "Analysis has not been run.");
1324 return LoanPropagation->getLoans(OID, PP);
1325}
1326
1327std::vector<LoanID>
1329 assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run.");
1330 std::vector<LoanID> Result;
1331 for (const auto &pair : ExpiredLoans->getExpiredLoans(PP))
1332 Result.push_back(pair.first);
1333 return Result;
1334}
1335
1336std::optional<OriginID>
1338 assert(FactMgr && "FactManager not initialized");
1339 // This assumes the OriginManager's `get` can find an existing origin.
1340 // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
1341 // in a const-query context if that becomes an issue.
1342 return FactMgr->getOriginMgr().get(*D);
1343}
1344
1345std::vector<LoanID>
1347 assert(FactMgr && "FactManager not initialized");
1348 std::vector<LoanID> Result;
1349 for (const Loan &L : FactMgr->getLoanMgr().getLoans())
1350 if (L.Path.D == VD)
1351 Result.push_back(L.ID);
1352 return Result;
1353}
1354
1355llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
1356 assert(FactMgr && "FactManager not initialized");
1357 llvm::StringMap<ProgramPoint> AnnotationToPointMap;
1358 for (const CFGBlock *Block : *AC.getCFG()) {
1359 for (const Fact *F : FactMgr->getFacts(Block)) {
1360 if (const auto *TPF = F->getAs<TestPointFact>()) {
1361 StringRef PointName = TPF->getAnnotation();
1362 assert(AnnotationToPointMap.find(PointName) ==
1363 AnnotationToPointMap.end() &&
1364 "more than one test points with the same name");
1365 AnnotationToPointMap[PointName] = F;
1366 }
1367 }
1368 }
1369 return AnnotationToPointMap;
1370}
1371} // namespace internal
1372
1374 LifetimeSafetyReporter *Reporter) {
1375 internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
1376 Analysis.run();
1377}
1378} // namespace clang::lifetimes
#define V(N, I)
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
C Language Family Type Representation.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
A builtin binary operation expression such as "x + y" or "x <= y".
Definition Expr.h:3972
Expr * getLHS() const
Definition Expr.h:4022
Expr * getRHS() const
Definition Expr.h:4024
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4108
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
Definition CFG.h:418
const VarDecl * getVarDecl() const
Definition CFG.h:423
const Stmt * getTriggerStmt() const
Definition CFG.h:428
Represents a single basic block in a source-level CFG.
Definition CFG.h:605
succ_range succs()
Definition CFG.h:1000
void dump() const
Definition CFG.cpp:6255
Represents a top-level expression in a basic block.
Definition CFG.h:55
std::optional< T > getAs() const
Convert to the specified CFGElement type, returning std::nullopt if this CFGElement is not of the des...
Definition CFG.h:109
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1222
void dump(const LangOptions &LO, bool ShowColors) const
dump - A simple pretty printer of a CFG that outputs to stderr.
Definition CFG.cpp:6219
Represents a call to a C++ constructor.
Definition ExprCXX.h:1549
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Definition ExprCXX.h:1833
Represents a call to a member function that may be written either with member call syntax (e....
Definition ExprCXX.h:179
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Definition ExprCXX.cpp:741
The null pointer literal (C++11 [lex.nullptr])
Definition ExprCXX.h:768
A call to an overloaded operator written using operator syntax.
Definition ExprCXX.h:84
static bool isAssignmentOp(OverloadedOperatorKind Opc)
Definition ExprCXX.h:119
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3068
Decl * getCalleeDecl()
Definition Expr.h:3054
Expr * getSubExpr()
Definition Expr.h:3660
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1270
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition Stmt.h:1611
decl_range decls()
Definition Stmt.h:1659
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition Expr.h:3787
Describes an C or C++ initializer list.
Definition Expr.h:5233
unsigned getNumInits() const
Definition Expr.h:5263
const Expr * getInit(unsigned Init) const
Definition Expr.h:5287
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Definition ExprCXX.h:4914
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
Definition ExprCXX.h:4931
A (possibly-)qualified type.
Definition TypeBase.h:937
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition Stmt.h:3160
Expr * getRetValue()
Definition Stmt.h:3187
Encodes a location in the source.
RetTy Visit(PTR(Stmt) S, ParamTys... P)
Definition StmtVisitor.h:45
SourceLocation getEndLoc() const LLVM_READONLY
Definition Stmt.cpp:358
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isPointerOrReferenceType() const
Definition TypeBase.h:8526
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition Expr.h:2244
Expr * getSubExpr() const
Definition Expr.h:2285
Opcode getOpcode() const
Definition Expr.h:2280
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:711
Represents a variable declaration or definition.
Definition Decl.h:925
AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
Lattice getInState(const CFGBlock *B) const
Lattice transfer(Lattice In, const TestPointFact &)
Lattice getOutState(const CFGBlock *B) const
DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F)
Lattice transfer(Lattice In, const ReturnOfOriginFact &)
Lattice transfer(Lattice In, const UseFact &)
Lattice transfer(Lattice In, const IssueFact &)
Lattice transfer(Lattice In, const AssignOriginFact &)
Lattice transfer(Lattice In, const ExpireFact &)
DataflowAnalysis< Derived, Lattice, Dir > Base
ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
void dump(llvm::raw_ostream &OS, const LoanManager &LM, const OriginManager &) const override
The analysis that tracks which loans have expired.
Lattice transfer(Lattice In, const ExpireFact &F)
Lattice transfer(Lattice In, const IssueFact &F)
ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, LifetimeFactory &Factory)
Lattice join(Lattice L1, Lattice L2)
Merges two lattices by taking the union of the two expired loans.
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE)
void VisitUnaryOperator(const UnaryOperator *UO)
void VisitInitListExpr(const InitListExpr *ILE)
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N)
void VisitImplicitCastExpr(const ImplicitCastExpr *ICE)
void VisitBinaryOperator(const BinaryOperator *BO)
void VisitCXXConstructExpr(const CXXConstructExpr *CCE)
void handleDestructor(const CFGAutomaticObjDtor &DtorOpt)
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE)
FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE)
void VisitDeclRefExpr(const DeclRefExpr *DRE)
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE)
llvm::ArrayRef< const Fact * > getFacts(const CFGBlock *B) const
void dump(const CFG &Cfg, AnalysisDeclContext &AC) const
void addBlockFacts(const CFGBlock *B, llvm::ArrayRef< Fact * > NewFacts)
An abstract base class for a single, atomic lifetime-relevant event.
@ TestPoint
A marker for a specific point in the code, for testing.
@ Expire
A loan expires as its underlying storage is freed (e.g., variable goes out of scope).
@ ReturnOfOrigin
An origin escapes the function by flowing into the return value.
@ Issue
A new loan is issued from a borrow expression (e.g., &x).
@ Use
An origin is used (eg. dereferencing a pointer).
@ AssignOrigin
An origin is propagated from a source to a destination (e.g., p = q).
virtual void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &) const
void dump(llvm::raw_ostream &OS, const LoanManager &LM, const OriginManager &OM) const override
LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA, FactManager &FM, AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
void checkUse(const UseFact *UF)
Checks for use-after-free errors for a given use of an Origin.
Running the lifetime safety analysis and querying its results.
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const
Returns the set of loans an origin holds at a specific program point.
std::optional< OriginID > getOriginIDForDecl(const ValueDecl *D) const
Finds the OriginID for a given declaration.
std::vector< LoanID > getExpiredLoansAtPoint(ProgramPoint PP) const
Returns the set of loans that have expired at a specific program point.
std::vector< LoanID > getLoanIDForVar(const VarDecl *VD) const
Finds the LoanID's for the loan created with the specific variable as their Path.
llvm::StringMap< ProgramPoint > getTestPoints() const
Retrieves program points that were specially marked in the source code for testing.
LifetimeSafetyAnalysis(AnalysisDeclContext &AC, LifetimeSafetyReporter *Reporter)
Manages the creation, storage and retrieval of loans.
const Loan & getLoan(LoanID ID) const
Loan & addLoan(AccessPath Path, const Expr *IssueExpr)
llvm::ArrayRef< Loan > getLoans() const
The analysis that tracks which loans belong to which origins.
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, LifetimeFactory &LFactory)
Lattice transfer(Lattice In, const IssueFact &F)
A new loan is issued to the origin. Old loans are erased.
Lattice transfer(Lattice In, const AssignOriginFact &F)
The destination origin's loan set is replaced by the source's.
Lattice join(Lattice A, Lattice B)
Merges two lattices by taking the union of loans for each origin.
LoanSet getLoans(OriginID OID, ProgramPoint P)
Manages the creation, storage, and retrieval of origins for pointer-like variables and expressions.
OriginID getOrCreate(const ValueDecl &D)
const Origin & getOrigin(OriginID ID) const
Origin & addOrigin(OriginID ID, const clang::Expr &E)
llvm::ArrayRef< Origin > getOrigins() const
void dump(OriginID OID, llvm::raw_ostream &OS) const
Origin & addOrigin(OriginID ID, const clang::ValueDecl &D)
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.
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &) const override
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
OriginID getUsedOrigin(const OriginManager &OM) const
static bool classof(const Fact *F)
static bool isSubsetOf(const llvm::ImmutableSet< T > &A, const llvm::ImmutableSet< T > &B)
Checks if set A is a subset of set B.
static llvm::ImmutableSet< T > join(llvm::ImmutableSet< T > A, llvm::ImmutableSet< T > B, typename llvm::ImmutableSet< T >::Factory &F)
Computes the union of two ImmutableSets.
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
llvm::ImmutableSet< LoanID > LoanSet
llvm::ImmutableMap< OriginID, LoanSet > OriginLoanMap
ID< struct OriginTag > OriginID
llvm::ImmutableMap< LoanID, const ExpireFact * > ExpiredLoanMap
ID< struct LoanTag > LoanID
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, LifetimeSafetyReporter *Reporter)
The main entry point for the analysis.
Confidence
Enum to track the confidence level of a potential error.
bool isa(CodeGen::Address addr)
Definition Address.h:330
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Result
The result type of a method or function.
Definition TypeBase.h:905
const FunctionProtoType * T
@ Other
Other implicit parameter.
Definition Decl.h:1745
A worklist implementation for backward dataflow analysis.
A worklist implementation for forward dataflow analysis.
Represents the storage location being borrowed, e.g., a specific stack variable.
AccessPath(const clang::ValueDecl *D)
ExpiredLoanMap Expired
Map from an expired LoanID to the ExpireFact that made it expire.
bool operator==(const ExpiredLattice &Other) const
void dump(llvm::raw_ostream &OS) const
bool operator!=(const ExpiredLattice &Other) const
A generic, type-safe wrapper for an ID, distinguished by its Tag type.
An object to hold the factories for immutable collections, ensuring that all created states share the...
Represents the dataflow lattice for loan propagation.
bool operator!=(const LoanPropagationLattice &Other) const
OriginLoanMap Origins
The map from an origin to the set of loans it contains.
bool operator==(const LoanPropagationLattice &Other) const
Information about a single borrow, or "Loan".
Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
const Expr * IssueExpr
The expression that creates the loan, e.g., &x.
LoanID ID
TODO: Represent opaque loans.
void dump(llvm::raw_ostream &OS) const
An Origin is a symbolic identifier that represents the set of possible loans a pointer-like object co...
Origin(OriginID ID, const clang::Expr *E)
const clang::Expr * getExpr() const
const clang::ValueDecl * getDecl() const
llvm::PointerUnion< const clang::ValueDecl *, const clang::Expr * > Ptr
A pointer to the AST node that this origin represents.
Origin(OriginID ID, const clang::ValueDecl *D)
Struct to store the complete context for a potential lifetime violation.