clang 23.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/Decl.h"
16#include "clang/AST/Expr.h"
26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
29
31
33 switch (K) {
37 return Confidence::Maybe;
39 return Confidence::None;
40 }
41 llvm_unreachable("unknown liveness kind");
42}
43
44namespace {
45
46/// Struct to store the complete context for a potential lifetime violation.
47struct PendingWarning {
48 SourceLocation ExpiryLoc; // Where the loan expired.
49 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
50 Confidence ConfidenceLevel;
51};
52
53using AnnotationTarget =
54 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
55using EscapingTarget = llvm::PointerUnion<const Expr *, const FieldDecl *>;
56
57class LifetimeChecker {
58private:
59 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
60 llvm::DenseMap<AnnotationTarget, const Expr *> AnnotationWarningsMap;
61 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
62 const LoanPropagationAnalysis &LoanPropagation;
63 const LiveOriginsAnalysis &LiveOrigins;
64 const FactManager &FactMgr;
65 LifetimeSafetySemaHelper *SemaHelper;
66 ASTContext &AST;
67
68public:
69 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
70 const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM,
72 LifetimeSafetySemaHelper *SemaHelper)
73 : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),
74 SemaHelper(SemaHelper), AST(ADC.getASTContext()) {
75 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
76 for (const Fact *F : FactMgr.getFacts(B))
77 if (const auto *EF = F->getAs<ExpireFact>())
78 checkExpiry(EF);
79 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
80 checkAnnotations(OEF);
81 issuePendingWarnings();
82 suggestAnnotations();
83 reportNoescapeViolations();
84 // Annotation inference is currently guarded by a frontend flag. In the
85 // future, this might be replaced by a design that differentiates between
86 // explicit and inferred findings with separate warning groups.
87 if (AST.getLangOpts().EnableLifetimeSafetyInference)
88 inferAnnotations();
89 }
90
91 /// Checks if an escaping origin holds a placeholder loan, indicating a
92 /// missing [[clang::lifetimebound]] annotation or a violation of
93 /// [[clang::noescape]].
94 void checkAnnotations(const OriginEscapesFact *OEF) {
95 OriginID EscapedOID = OEF->getEscapedOriginID();
96 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
97 auto CheckParam = [&](const ParmVarDecl *PVD) {
98 // NoEscape param should not escape.
99 if (PVD->hasAttr<NoEscapeAttr>()) {
100 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
101 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
102 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
103 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
104 return;
105 }
106 // Suggest lifetimebound for parameter escaping through return.
107 if (!PVD->hasAttr<LifetimeBoundAttr>())
108 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
109 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
110 // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
111 // field!
112 };
113 auto CheckImplicitThis = [&](const CXXMethodDecl *MD) {
115 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
116 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
117 };
118 for (LoanID LID : EscapedLoans) {
119 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
120 const auto *PL = dyn_cast<PlaceholderLoan>(L);
121 if (!PL)
122 continue;
123 if (const auto *PVD = PL->getParmVarDecl())
124 CheckParam(PVD);
125 else if (const auto *MD = PL->getMethodDecl())
126 CheckImplicitThis(MD);
127 }
128 }
129
130 /// Checks for use-after-free & use-after-return errors when a loan expires.
131 ///
132 /// This method examines all live origins at the expiry point and determines
133 /// if any of them hold the expiring loan. If so, it creates a pending
134 /// warning with the appropriate confidence level based on the liveness
135 /// information. The confidence reflects whether the origin is definitely
136 /// or maybe live at this point.
137 ///
138 /// Note: This implementation considers only the confidence of origin
139 /// liveness. Future enhancements could also consider the confidence of loan
140 /// propagation (e.g., a loan may only be held on some execution paths).
141 void checkExpiry(const ExpireFact *EF) {
142 LoanID ExpiredLoan = EF->getLoanID();
143 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
144 Confidence CurConfidence = Confidence::None;
145 // The UseFact or OriginEscapesFact most indicative of a lifetime error,
146 // prioritized by earlier source location.
147 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
148 BestCausingFact = nullptr;
149
150 for (auto &[OID, LiveInfo] : Origins) {
151 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
152 if (!HeldLoans.contains(ExpiredLoan))
153 continue;
154 // Loan is defaulted.
155 Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
156 if (CurConfidence < NewConfidence) {
157 CurConfidence = NewConfidence;
158 BestCausingFact = LiveInfo.CausingFact;
159 }
160 }
161 if (!BestCausingFact)
162 return;
163 // We have a use-after-free.
164 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
165 if (LastConf >= CurConfidence)
166 return;
167 FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
168 /*BestCausingFact=*/BestCausingFact,
169 /*ConfidenceLevel=*/CurConfidence};
170 }
171
172 void issuePendingWarnings() {
173 if (!SemaHelper)
174 return;
175 for (const auto &[LID, Warning] : FinalWarningsMap) {
176 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
177 const auto *BL = cast<PathLoan>(L);
178 const Expr *IssueExpr = BL->getIssueExpr();
179 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
180 CausingFact = Warning.CausingFact;
181 Confidence Confidence = Warning.ConfidenceLevel;
182 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
183
184 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>())
185 SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc,
186 Confidence);
187 else if (const auto *OEF =
188 CausingFact.dyn_cast<const OriginEscapesFact *>()) {
189 if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
190 SemaHelper->reportUseAfterReturn(
191 IssueExpr, RetEscape->getReturnExpr(), ExpiryLoc, Confidence);
192 else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
193 SemaHelper->reportDanglingField(
194 IssueExpr, FieldEscape->getFieldDecl(), ExpiryLoc);
195 else
196 llvm_unreachable("Unhandled OriginEscapesFact type");
197 } else
198 llvm_unreachable("Unhandled CausingFact type");
199 }
200 }
201
202 /// Returns the declaration of a function that is visible across translation
203 /// units, if such a declaration exists and is different from the definition.
204 static const FunctionDecl *getCrossTUDecl(const FunctionDecl &FD,
205 SourceManager &SM) {
206 if (!FD.isExternallyVisible())
207 return nullptr;
208 const FileID DefinitionFile = SM.getFileID(FD.getLocation());
209 for (const FunctionDecl *Redecl : FD.redecls())
210 if (SM.getFileID(Redecl->getLocation()) != DefinitionFile)
211 return Redecl;
212
213 return nullptr;
214 }
215
216 static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD,
217 SourceManager &SM) {
218 if (const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
219 return getCrossTUDecl(*FD, SM);
220 return nullptr;
221 }
222
223 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
224 const ParmVarDecl *PVD,
225 SourceManager &SM,
226 const Expr *EscapeExpr) {
227 if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM))
228 SemaHelper->suggestLifetimeboundToParmVar(
230 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr);
231 else
232 SemaHelper->suggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD,
233 EscapeExpr);
234 }
235
236 static void
237 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
238 const CXXMethodDecl *MD, SourceManager &SM,
239 const Expr *EscapeExpr) {
240 if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM))
241 SemaHelper->suggestLifetimeboundToImplicitThis(
243 EscapeExpr);
244 else
245 SemaHelper->suggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU,
246 MD, EscapeExpr);
247 }
248
249 void suggestAnnotations() {
250 if (!SemaHelper)
251 return;
252 SourceManager &SM = AST.getSourceManager();
253 for (auto [Target, EscapeExpr] : AnnotationWarningsMap) {
254 if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
255 suggestWithScopeForParmVar(SemaHelper, PVD, SM, EscapeExpr);
256 else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>())
257 suggestWithScopeForImplicitThis(SemaHelper, MD, SM, EscapeExpr);
258 }
259 }
260
261 void reportNoescapeViolations() {
262 for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
263 if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
264 SemaHelper->reportNoescapeViolation(PVD, E);
265 else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
266 SemaHelper->reportNoescapeViolation(PVD, FD);
267 else
268 llvm_unreachable("Unhandled EscapingTarget type");
269 }
270 }
271
272 void inferAnnotations() {
273 for (auto [Target, EscapeExpr] : AnnotationWarningsMap) {
274 if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
276 SemaHelper->addLifetimeBoundToImplicitThis(cast<CXXMethodDecl>(MD));
277 } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) {
278 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
279 if (!FD)
280 continue;
281 // Propagates inferred attributes via the most recent declaration to
282 // ensure visibility for callers in post-order analysis.
284 ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
285 FD->getParamDecl(PVD->getFunctionScopeIndex()));
286 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
287 InferredPVD->addAttr(
288 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
289 }
290 }
291 }
292};
293} // namespace
294
296 const LiveOriginsAnalysis &LO,
297 const FactManager &FactMgr, AnalysisDeclContext &ADC,
298 LifetimeSafetySemaHelper *SemaHelper) {
299 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
300 LifetimeChecker Checker(LP, LO, FactMgr, ADC, SemaHelper);
301}
302
303} // namespace clang::lifetimes::internal
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
#define SM(sm)
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
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:944
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.
Abstract interface for operations requiring Sema access.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
Definition Loans.h:25
utils::ID< struct OriginTag > OriginID
Definition Origins.h:27
llvm::ImmutableMap< OriginID, LivenessInfo > LivenessMap
Definition LiveOrigins.h:76
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
Definition Checker.cpp:295
static Confidence livenessKindToConfidence(LivenessKind K)
Definition Checker.cpp:32
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
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