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