clang 22.0.0git
Checker.cpp
Go to the documentation of this file.
1//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- 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 implements the LifetimeChecker, which detects use-after-free
10// errors by checking if live origins hold loans that have expired.
11//
12//===----------------------------------------------------------------------===//
13
15#include "clang/AST/Expr.h"
23#include "llvm/ADT/DenseMap.h"
24#include "llvm/Support/ErrorHandling.h"
25#include "llvm/Support/TimeProfiler.h"
26
28
30 switch (K) {
34 return Confidence::Maybe;
36 return Confidence::None;
37 }
38 llvm_unreachable("unknown liveness kind");
39}
40
41namespace {
42
43/// Struct to store the complete context for a potential lifetime violation.
44struct PendingWarning {
45 SourceLocation ExpiryLoc; // Where the loan expired.
46 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
47 Confidence ConfidenceLevel;
48};
49
50class LifetimeChecker {
51private:
52 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
53 llvm::DenseMap<const ParmVarDecl *, const Expr *> AnnotationWarningsMap;
54 const LoanPropagationAnalysis &LoanPropagation;
55 const LiveOriginsAnalysis &LiveOrigins;
56 const FactManager &FactMgr;
57 LifetimeSafetyReporter *Reporter;
58 ASTContext &AST;
59
60public:
61 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
62 const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM,
63 AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
64 : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),
65 Reporter(Reporter), AST(ADC.getASTContext()) {
66 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
67 for (const Fact *F : FactMgr.getFacts(B))
68 if (const auto *EF = F->getAs<ExpireFact>())
69 checkExpiry(EF);
70 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
71 checkAnnotations(OEF);
72 issuePendingWarnings();
73 suggestAnnotations();
74 // Annotation inference is currently guarded by a frontend flag. In the
75 // future, this might be replaced by a design that differentiates between
76 // explicit and inferred findings with separate warning groups.
77 if (AST.getLangOpts().EnableLifetimeSafetyInference)
78 inferAnnotations();
79 }
80
81 /// Checks if an escaping origin holds a placeholder loan, indicating a
82 /// missing [[clang::lifetimebound]] annotation.
83 void checkAnnotations(const OriginEscapesFact *OEF) {
84 OriginID EscapedOID = OEF->getEscapedOriginID();
85 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
86 for (LoanID LID : EscapedLoans) {
87 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
88 if (const auto *PL = dyn_cast<PlaceholderLoan>(L)) {
89 const ParmVarDecl *PVD = PL->getParmVarDecl();
90 if (PVD->hasAttr<LifetimeBoundAttr>())
91 continue;
92 AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr());
93 }
94 }
95 }
96
97 /// Checks for use-after-free & use-after-return errors when a loan expires.
98 ///
99 /// This method examines all live origins at the expiry point and determines
100 /// if any of them hold the expiring loan. If so, it creates a pending
101 /// warning with the appropriate confidence level based on the liveness
102 /// information. The confidence reflects whether the origin is definitely
103 /// or maybe live at this point.
104 ///
105 /// Note: This implementation considers only the confidence of origin
106 /// liveness. Future enhancements could also consider the confidence of loan
107 /// propagation (e.g., a loan may only be held on some execution paths).
108 void checkExpiry(const ExpireFact *EF) {
109 LoanID ExpiredLoan = EF->getLoanID();
110 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
111 Confidence CurConfidence = Confidence::None;
112 // The UseFact or OriginEscapesFact most indicative of a lifetime error,
113 // prioritized by earlier source location.
114 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
115 BestCausingFact = nullptr;
116
117 for (auto &[OID, LiveInfo] : Origins) {
118 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
119 if (!HeldLoans.contains(ExpiredLoan))
120 continue;
121 // Loan is defaulted.
122 Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
123 if (CurConfidence < NewConfidence) {
124 CurConfidence = NewConfidence;
125 BestCausingFact = LiveInfo.CausingFact;
126 }
127 }
128 if (!BestCausingFact)
129 return;
130 // We have a use-after-free.
131 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
132 if (LastConf >= CurConfidence)
133 return;
134 FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
135 /*BestCausingFact=*/BestCausingFact,
136 /*ConfidenceLevel=*/CurConfidence};
137 }
138
139 void issuePendingWarnings() {
140 if (!Reporter)
141 return;
142 for (const auto &[LID, Warning] : FinalWarningsMap) {
143 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
144 const auto *BL = cast<PathLoan>(L);
145 const Expr *IssueExpr = BL->getIssueExpr();
146 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
147 CausingFact = Warning.CausingFact;
148 Confidence Confidence = Warning.ConfidenceLevel;
149 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
150
151 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>())
152 Reporter->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc,
153 Confidence);
154 else if (const auto *OEF =
155 CausingFact.dyn_cast<const OriginEscapesFact *>())
156 Reporter->reportUseAfterReturn(IssueExpr, OEF->getEscapeExpr(),
157 ExpiryLoc, Confidence);
158 else
159 llvm_unreachable("Unhandled CausingFact type");
160 }
161 }
162
163 void suggestAnnotations() {
164 if (!Reporter)
165 return;
166 for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap)
167 Reporter->suggestAnnotation(PVD, EscapeExpr);
168 }
169
170 void inferAnnotations() {
171 // FIXME: To maximise inference propagation, functions should be analyzed in
172 // post-order of the call graph, allowing inferred annotations to propagate
173 // through the call chain
174 // FIXME: Add the inferred attribute to all redeclarations of the function,
175 // not just the definition being analyzed.
176 for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) {
177 ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD);
178 if (!PVD->hasAttr<LifetimeBoundAttr>())
179 PVD->addAttr(
180 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
181 }
182 }
183};
184} // namespace
185
187 const LiveOriginsAnalysis &LO,
188 const FactManager &FactMgr, AnalysisDeclContext &ADC,
189 LifetimeSafetyReporter *Reporter) {
190 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
191 LifetimeChecker Checker(LP, LO, FactMgr, ADC, Reporter);
192}
193
194} // namespace clang::lifetimes::internal
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
const LangOptions & getLangOpts() const
Definition ASTContext.h:945
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a single basic block in a source-level CFG.
Definition CFG.h:605
Encodes a location in the source.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
Definition Loans.h:23
utils::ID< struct OriginTag > OriginID
Definition Origins.h:23
llvm::ImmutableMap< OriginID, LivenessInfo > LivenessMap
Definition LiveOrigins.h:76
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
Definition Checker.cpp:186
static Confidence livenessKindToConfidence(LivenessKind K)
Definition Checker.cpp:29
Confidence
Enum to track the confidence level of a potential error.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
U cast(CodeGen::Address addr)
Definition Address.h:327