26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
40 llvm_unreachable(
"unknown liveness kind");
46struct PendingWarning {
48 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
49 const Expr *MovedExpr;
50 const Expr *InvalidatedByExpr;
51 bool CausingFactDominatesExpiry;
54using AnnotationTarget =
55 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
57 llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
59class LifetimeChecker {
61 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
62 llvm::DenseMap<AnnotationTarget, const Expr *> AnnotationWarningsMap;
63 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
64 const LoanPropagationAnalysis &LoanPropagation;
65 const MovedLoansAnalysis &MovedLoans;
66 const LiveOriginsAnalysis &LiveOrigins;
68 LifetimeSafetySemaHelper *SemaHelper;
72 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
73 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
74 return UF->getUseExpr()->getExprLoc();
75 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
76 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
77 return ReturnEsc->getReturnExpr()->getExprLoc();
78 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
79 return FieldEsc->getFieldDecl()->getLocation();
81 llvm_unreachable(
"unhandled causing fact in PointerUnion");
85 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
86 const MovedLoansAnalysis &MovedLoans,
87 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
88 AnalysisDeclContext &ADC,
89 LifetimeSafetySemaHelper *SemaHelper)
90 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
91 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
92 AST(ADC.getASTContext()) {
93 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
94 for (
const Fact *F : FactMgr.getFacts(B))
95 if (
const auto *EF = F->getAs<ExpireFact>())
97 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
98 checkInvalidation(IOF);
99 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
100 checkAnnotations(OEF);
101 issuePendingWarnings();
102 suggestAnnotations();
103 reportNoescapeViolations();
107 if (AST.getLangOpts().EnableLifetimeSafetyInference)
114 void checkAnnotations(
const OriginEscapesFact *OEF) {
115 OriginID EscapedOID = OEF->getEscapedOriginID();
116 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
117 auto CheckParam = [&](
const ParmVarDecl *PVD) {
119 if (PVD->hasAttr<NoEscapeAttr>()) {
120 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
121 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
122 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
123 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
124 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
125 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
129 if (!PVD->hasAttr<LifetimeBoundAttr>())
130 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
131 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
135 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
137 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
138 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
140 for (
LoanID LID : EscapedLoans) {
141 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
142 const AccessPath &AP = L->getAccessPath();
143 if (
const auto *PVD = AP.getAsPlaceholderParam())
145 else if (
const auto *MD = AP.getAsPlaceholderThis())
146 CheckImplicitThis(MD);
156 void checkExpiry(
const ExpireFact *EF) {
157 const AccessPath &ExpiredPath = EF->getAccessPath();
158 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
159 for (
auto &[OID, LiveInfo] : Origins) {
160 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
161 for (
LoanID HeldLoanID : HeldLoans) {
162 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
163 if (ExpiredPath != HeldLoan->getAccessPath())
166 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
167 const Expr *MovedExpr =
nullptr;
168 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
171 if (CurWarning.CausingFactDominatesExpiry)
174 CurWarning.CausingFactDominatesExpiry =
true;
175 CurWarning.CausingFact = LiveInfo.CausingFact;
176 CurWarning.ExpiryLoc = EF->getExpiryLoc();
177 CurWarning.MovedExpr = MovedExpr;
178 CurWarning.InvalidatedByExpr =
nullptr;
188 void checkInvalidation(
const InvalidateOriginFact *IOF) {
189 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
191 LoanSet DirectlyInvalidatedLoans =
192 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
193 auto IsInvalidated = [&](
const Loan *L) {
194 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
195 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
196 if (InvalidL->getAccessPath() == L->getAccessPath())
202 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
203 for (
auto &[OID, LiveInfo] : Origins) {
204 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
205 for (
LoanID LiveLoanID : HeldLoans)
206 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
208 bool LastDomination =
209 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
210 if (!LastDomination) {
211 FinalWarningsMap[LiveLoanID] = {
213 LiveInfo.CausingFact,
215 IOF->getInvalidationExpr(),
222 void issuePendingWarnings() {
225 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
226 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
227 const Expr *IssueExpr = L->getIssuingExpr();
228 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
229 CausingFact =
Warning.CausingFact;
230 const ParmVarDecl *InvalidatedPVD =
231 L->getAccessPath().getAsPlaceholderParam();
232 const Expr *MovedExpr =
Warning.MovedExpr;
233 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
235 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
236 if (
Warning.InvalidatedByExpr) {
239 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
241 else if (InvalidatedPVD)
243 SemaHelper->reportUseAfterInvalidation(
244 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
248 SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
250 }
else if (
const auto *OEF =
251 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
252 if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
254 SemaHelper->reportUseAfterReturn(
255 IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
256 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
258 SemaHelper->reportDanglingField(
259 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
260 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
262 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
263 MovedExpr, ExpiryLoc);
265 llvm_unreachable(
"Unhandled OriginEscapesFact type");
267 llvm_unreachable(
"Unhandled CausingFact type");
273 static const FunctionDecl *getCrossTUDecl(
const FunctionDecl &FD,
275 if (!FD.isExternallyVisible())
277 const FileID DefinitionFile =
SM.getFileID(FD.getLocation());
278 for (
const FunctionDecl *Redecl : FD.redecls())
279 if (
SM.getFileID(Redecl->getLocation()) != DefinitionFile)
285 static const FunctionDecl *getCrossTUDecl(
const ParmVarDecl &PVD,
287 if (
const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
288 return getCrossTUDecl(*FD,
SM);
292 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
293 const ParmVarDecl *PVD,
295 const Expr *EscapeExpr) {
296 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD,
SM))
297 SemaHelper->suggestLifetimeboundToParmVar(
299 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr);
306 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
307 const CXXMethodDecl *MD, SourceManager &
SM,
308 const Expr *EscapeExpr) {
309 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD,
SM))
310 SemaHelper->suggestLifetimeboundToImplicitThis(
318 void suggestAnnotations() {
321 SourceManager &
SM = AST.getSourceManager();
322 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
323 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
324 suggestWithScopeForParmVar(SemaHelper, PVD,
SM, EscapeExpr);
325 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>())
326 suggestWithScopeForImplicitThis(SemaHelper, MD,
SM, EscapeExpr);
330 void reportNoescapeViolations() {
331 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
332 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
333 SemaHelper->reportNoescapeViolation(PVD, E);
334 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
335 SemaHelper->reportNoescapeViolation(PVD, FD);
336 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
337 SemaHelper->reportNoescapeViolation(PVD, G);
339 llvm_unreachable(
"Unhandled EscapingTarget type");
343 void inferAnnotations() {
344 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
345 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
348 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
349 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
355 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
356 FD->getParamDecl(PVD->getFunctionScopeIndex()));
357 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
358 InferredPVD->addAttr(
359 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
371 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
372 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper);
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
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 ...
AnalysisDeclContext contains the context data for the function, method or block under analysis.
This represents one expression.
Encodes a location in the source.
Abstract interface for operations requiring Sema access.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
static bool causingFactDominatesExpiry(LivenessKind K)
llvm::ImmutableMap< OriginID, LivenessInfo > LivenessMap
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const MovedLoansAnalysis &MovedLoans, const LiveOriginsAnalysis &LiveOrigins, FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
U cast(CodeGen::Address addr)