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/Expr.h"
25#include "llvm/ADT/DenseMap.h"
26#include "llvm/Support/ErrorHandling.h"
27#include "llvm/Support/TimeProfiler.h"
28
30
32 switch (K) {
36 return Confidence::Maybe;
38 return Confidence::None;
39 }
40 llvm_unreachable("unknown liveness kind");
41}
42
43namespace {
44
45/// Struct to store the complete context for a potential lifetime violation.
46struct PendingWarning {
47 SourceLocation ExpiryLoc; // Where the loan expired.
48 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
49 Confidence ConfidenceLevel;
50};
51
52class LifetimeChecker {
53private:
54 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
55 llvm::DenseMap<const ParmVarDecl *, const Expr *> AnnotationWarningsMap;
56 const LoanPropagationAnalysis &LoanPropagation;
57 const LiveOriginsAnalysis &LiveOrigins;
58 const FactManager &FactMgr;
59 LifetimeSafetyReporter *Reporter;
60 ASTContext &AST;
61
62public:
63 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
64 const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM,
65 AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
66 : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),
67 Reporter(Reporter), AST(ADC.getASTContext()) {
68 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
69 for (const Fact *F : FactMgr.getFacts(B))
70 if (const auto *EF = F->getAs<ExpireFact>())
71 checkExpiry(EF);
72 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
73 checkAnnotations(OEF);
74 issuePendingWarnings();
75 suggestAnnotations();
76 // Annotation inference is currently guarded by a frontend flag. In the
77 // future, this might be replaced by a design that differentiates between
78 // explicit and inferred findings with separate warning groups.
79 if (AST.getLangOpts().EnableLifetimeSafetyInference)
80 inferAnnotations();
81 }
82
83 /// Checks if an escaping origin holds a placeholder loan, indicating a
84 /// missing [[clang::lifetimebound]] annotation.
85 void checkAnnotations(const OriginEscapesFact *OEF) {
86 OriginID EscapedOID = OEF->getEscapedOriginID();
87 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
88 for (LoanID LID : EscapedLoans) {
89 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
90 if (const auto *PL = dyn_cast<PlaceholderLoan>(L)) {
91 const ParmVarDecl *PVD = PL->getParmVarDecl();
92 if (PVD->hasAttr<LifetimeBoundAttr>())
93 continue;
94 AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr());
95 }
96 }
97 }
98
99 /// Checks for use-after-free & use-after-return errors when a loan expires.
100 ///
101 /// This method examines all live origins at the expiry point and determines
102 /// if any of them hold the expiring loan. If so, it creates a pending
103 /// warning with the appropriate confidence level based on the liveness
104 /// information. The confidence reflects whether the origin is definitely
105 /// or maybe live at this point.
106 ///
107 /// Note: This implementation considers only the confidence of origin
108 /// liveness. Future enhancements could also consider the confidence of loan
109 /// propagation (e.g., a loan may only be held on some execution paths).
110 void checkExpiry(const ExpireFact *EF) {
111 LoanID ExpiredLoan = EF->getLoanID();
112 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
113 Confidence CurConfidence = Confidence::None;
114 // The UseFact or OriginEscapesFact most indicative of a lifetime error,
115 // prioritized by earlier source location.
116 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
117 BestCausingFact = nullptr;
118
119 for (auto &[OID, LiveInfo] : Origins) {
120 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
121 if (!HeldLoans.contains(ExpiredLoan))
122 continue;
123 // Loan is defaulted.
124 Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
125 if (CurConfidence < NewConfidence) {
126 CurConfidence = NewConfidence;
127 BestCausingFact = LiveInfo.CausingFact;
128 }
129 }
130 if (!BestCausingFact)
131 return;
132 // We have a use-after-free.
133 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
134 if (LastConf >= CurConfidence)
135 return;
136 FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
137 /*BestCausingFact=*/BestCausingFact,
138 /*ConfidenceLevel=*/CurConfidence};
139 }
140
141 void issuePendingWarnings() {
142 if (!Reporter)
143 return;
144 for (const auto &[LID, Warning] : FinalWarningsMap) {
145 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
146 const auto *BL = cast<PathLoan>(L);
147 const Expr *IssueExpr = BL->getIssueExpr();
148 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
149 CausingFact = Warning.CausingFact;
150 Confidence Confidence = Warning.ConfidenceLevel;
151 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
152
153 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>())
154 Reporter->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc,
155 Confidence);
156 else if (const auto *OEF =
157 CausingFact.dyn_cast<const OriginEscapesFact *>())
158 Reporter->reportUseAfterReturn(IssueExpr, OEF->getEscapeExpr(),
159 ExpiryLoc, Confidence);
160 else
161 llvm_unreachable("Unhandled CausingFact type");
162 }
163 }
164
165 /// Returns the declaration of a function that is visible across translation
166 /// units, if such a declaration exists and is different from the definition.
167 static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD,
168 SourceManager &SM) {
169 const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext());
170 if (!FD)
171 return nullptr;
172 if (!FD->isExternallyVisible())
173 return nullptr;
174 const FileID DefinitionFile = SM.getFileID(FD->getLocation());
175 for (const FunctionDecl *Redecl : FD->redecls())
176 if (SM.getFileID(Redecl->getLocation()) != DefinitionFile)
177 return Redecl;
178
179 return nullptr;
180 }
181
182 void suggestAnnotations() {
183 if (!Reporter)
184 return;
185 SourceManager &SM = AST.getSourceManager();
186 for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) {
187 if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM))
188 Reporter->suggestAnnotation(
190 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
191 EscapeExpr);
192 else
193 Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, EscapeExpr);
194 }
195 }
196
197 void inferAnnotations() {
198 for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) {
199 ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD);
200 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
201 if (!FD)
202 continue;
203 // Propagates inferred attributes via the most recent declaration to
204 // ensure visibility for callers in post-order analysis.
206 ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
207 FD->getParamDecl(PVD->getFunctionScopeIndex()));
208 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
209 InferredPVD->addAttr(
210 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
211 }
212 }
213};
214} // namespace
215
217 const LiveOriginsAnalysis &LO,
218 const FactManager &FactMgr, AnalysisDeclContext &ADC,
219 LifetimeSafetyReporter *Reporter) {
220 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
221 LifetimeChecker Checker(LP, LO, FactMgr, ADC, Reporter);
222}
223
224} // 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.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
Definition Loans.h:24
utils::ID< struct OriginTag > OriginID
Definition Origins.h:26
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:216
static Confidence livenessKindToConfidence(LivenessKind K)
Definition Checker.cpp:31
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