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 *>;
58class LifetimeChecker {
60 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
61 llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
62 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
63 llvm::DenseMap<const FieldDecl *, const Expr *> FieldOriginEscapesMap;
64 llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
65 const LoanPropagationAnalysis &LoanPropagation;
66 const MovedLoansAnalysis &MovedLoans;
67 const LiveOriginsAnalysis &LiveOrigins;
69 LifetimeSafetySemaHelper *SemaHelper;
73 const LifetimeSafetyOpts &LSOpts;
76 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
77 if (
const auto *UF = dyn_cast<const UseFact *>(F))
78 return UF->getUseExpr()->getExprLoc();
79 if (
const auto *OEF = dyn_cast<const OriginEscapesFact *>(F)) {
80 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
81 return ReturnEsc->getReturnExpr()->getExprLoc();
82 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
83 return FieldEsc->getFieldDecl()->getLocation();
85 llvm_unreachable(
"unhandled causing fact in PointerUnion");
89 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
90 const MovedLoansAnalysis &MovedLoans,
91 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
92 AnalysisDeclContext &ADC,
93 LifetimeSafetySemaHelper *SemaHelper,
94 const LifetimeSafetyOpts &LSOpts)
95 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
96 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
97 AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
99 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
100 for (
const Fact *F : FactMgr.getFacts(B))
101 if (
const auto *EF = F->getAs<ExpireFact>())
103 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
104 checkInvalidation(IOF);
105 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
106 checkAnnotations(OEF);
107 issuePendingWarnings();
108 suggestAnnotations();
109 if (LSOpts.CheckFieldOriginEscape)
110 reportFieldOriginEscapes();
111 if (LSOpts.CheckNoescapeViolations)
112 reportNoescapeViolations();
113 if (LSOpts.CheckLifetimeboundViolations)
114 reportLifetimeboundViolations();
115 if (LSOpts.CheckMisplacedLifetimebound)
116 reportMisplacedLifetimebound();
117 if (LSOpts.CheckInapplicableLifetimebound)
118 reportInapplicableLifetimebound();
122 if (AST.getLangOpts().EnableLifetimeSafetyInference)
129 void checkAnnotations(
const OriginEscapesFact *OEF) {
130 OriginID EscapedOID = OEF->getEscapedOriginID();
131 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
132 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
134 if (PVD->hasAttr<NoEscapeAttr>()) {
135 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
136 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
137 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
138 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
139 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
140 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
147 if (PVD->hasAttr<LifetimeBoundAttr>()) {
152 VerifiedLiftimeboundEscapes.insert(PVD);
156 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
157 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
158 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
163 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
169 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
170 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
172 VerifiedLiftimeboundEscapes.insert(MD);
174 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
179 auto CheckField = [&](
const FieldDecl *
Field) {
180 if (!LSOpts.CheckFieldOriginEscape)
183 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
186 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
189 if (OriginList *RetList = FactMgr.getOriginMgr().getOrCreateList(
190 ReturnEsc->getReturnExpr());
191 RetList && RetList->getOuterOriginID() == EscapedOID)
192 FieldOriginEscapesMap.try_emplace(Field, ReturnEsc->getReturnExpr());
195 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
196 for (
LoanID LID : EscapedLoans) {
197 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
198 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
201 if (
const auto *PVD = PB->getParmVarDecl())
202 CheckParam(PVD, MovedAtEscape.lookup(LID));
203 else if (
const auto *MD = PB->getImplicitThisParent())
204 CheckImplicitThis(MD);
205 else if (
const auto *Field = PB->getFieldDecl())
217 void checkExpiry(
const ExpireFact *EF) {
218 const AccessPath &ExpiredPath = EF->getAccessPath();
219 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
220 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
221 for (
auto &[OID, LiveInfo] : Live) {
222 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
223 for (
LoanID HeldLoanID : HeldLoans) {
224 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
225 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
228 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
229 const Expr *MovedExpr =
nullptr;
230 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
233 if (CurWarning.CausingFactDominatesExpiry)
236 CurWarning.CausingFactDominatesExpiry =
true;
237 CurWarning.CausingFact = LiveInfo.CausingFact;
238 CurWarning.ExpiryLoc = EF->getExpiryLoc();
239 CurWarning.MovedExpr = MovedExpr;
240 CurWarning.InvalidatedByExpr =
nullptr;
252 void checkInvalidation(
const InvalidateOriginFact *IOF) {
253 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
255 LoanSet DirectlyInvalidatedLoans =
256 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
257 auto IsInvalidated = [&](
const Loan *L) {
258 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
259 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
260 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
266 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
267 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
268 for (
auto &[OID, LiveInfo] : Live) {
269 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
270 for (
LoanID LiveLoanID : HeldLoans)
271 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
273 bool LastDomination =
274 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
275 if (!LastDomination) {
276 FinalWarningsMap[LiveLoanID] = {
278 LiveInfo.CausingFact,
280 IOF->getInvalidationExpr(),
287 void issuePendingWarnings() {
288 llvm::TimeTraceScope TimeTrace(
"IssuePendingWarnings");
291 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
292 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
293 const Expr *IssueExpr = L->getIssueExpr();
294 const ParmVarDecl *InvalidatedPVD =
nullptr;
295 if (
const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
296 InvalidatedPVD = PB->getParmVarDecl();
298 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
299 CausingFact =
Warning.CausingFact;
300 const Expr *MovedExpr =
Warning.MovedExpr;
301 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
303 if (
const auto *UF = dyn_cast<const UseFact *>(CausingFact)) {
304 llvm::SmallVector<const Expr *> ExprChain =
305 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
306 if (
Warning.InvalidatedByExpr) {
309 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
312 else if (InvalidatedPVD)
314 SemaHelper->reportUseAfterInvalidation(
315 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr,
320 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
321 MovedExpr, ExpiryLoc, ExprChain);
323 }
else if (
const auto *OEF =
324 dyn_cast<const OriginEscapesFact *>(CausingFact)) {
325 if (
Warning.InvalidatedByExpr) {
326 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
330 SemaHelper->reportInvalidatedField(IssueExpr,
331 FieldEscape->getFieldDecl(),
333 else if (InvalidatedPVD)
335 SemaHelper->reportInvalidatedField(InvalidatedPVD,
336 FieldEscape->getFieldDecl(),
338 }
else if (
const auto *GlobalEscape =
339 dyn_cast<GlobalEscapeFact>(OEF)) {
344 SemaHelper->reportInvalidatedGlobal(IssueExpr,
345 GlobalEscape->getGlobal(),
347 else if (InvalidatedPVD)
349 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
350 GlobalEscape->getGlobal(),
354 }
else if (
const auto *ThisEscape = dyn_cast<ThisEscapeFact>(OEF)) {
357 llvm::SmallVector<const Expr *> ExprChain = getExprChain(
358 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg));
360 SemaHelper->reportUseAfterInvalidation(
361 IssueExpr, ThisEscape->getLoc(),
Warning.InvalidatedByExpr,
363 else if (InvalidatedPVD)
364 SemaHelper->reportUseAfterInvalidation(
365 InvalidatedPVD, ThisEscape->getLoc(),
366 Warning.InvalidatedByExpr, ExprChain);
368 llvm_unreachable(
"Unhandled OriginEscapesFact type");
369 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
371 SemaHelper->reportUseAfterReturn(
372 IssueExpr, RetEscape->getReturnExpr(), MovedExpr,
374 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
375 }
else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
377 bool IsCapturedByLambda =
378 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
379 SemaHelper->reportDanglingField(
380 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
381 IsCapturedByLambda, ExpiryLoc);
382 }
else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
385 if (
const auto *
Func = dyn_cast_if_present<FunctionDecl>(FD))
386 IsMain =
Func->isMain();
387 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
388 MovedExpr, ExpiryLoc, IsMain);
389 }
else if (
const auto *ThisEscape = dyn_cast<ThisEscapeFact>(OEF))
394 SemaHelper->reportUseAfterScope(
395 IssueExpr, ThisEscape->getLoc(), MovedExpr, ExpiryLoc,
397 LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
399 llvm_unreachable(
"Unhandled OriginEscapesFact type");
401 llvm_unreachable(
"Unhandled CausingFact type");
410 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
411 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
415 assert(FDef->isThisDeclarationADefinition() &&
416 "Expected FunctionDecl to be a definition");
418 const auto &SM = FDef->getASTContext().getSourceManager();
420 auto GetFile = [&SM](
const FunctionDecl *FD) {
421 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
424 const FileID DefFile = GetFile(FDef);
425 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
426 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
427 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
433 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
434 FileID
File = GetFile(FD);
437 for (
auto [SeenFD, _] : Targets)
438 if (GetFile(SeenFD) ==
File)
447 auto redecls = llvm::to_vector(FDef->redecls());
449 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
450 AddCrossTUDecl(Redecl);
455 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
456 EscapingTarget EscapeTarget) {
457 if (llvm::isa<const VarDecl *>(EscapeTarget))
461 const auto *ParmToAnnotate =
462 Decl->getParamDecl(PVD->getFunctionScopeIndex());
463 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
468 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
469 const Expr *EscapeExpr) {
470 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
471 SemaHelper->suggestLifetimeboundToImplicitThis(
476 void suggestAnnotations() {
479 if (!LSOpts.SuggestAnnotations)
481 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
482 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
483 if (
const auto *PVD = dyn_cast<const ParmVarDecl *>(
Target))
484 suggestWithScopeForParmVar(PVD, EscapeTarget);
485 else if (
const auto *MD = dyn_cast<const CXXMethodDecl *>(
Target)) {
486 if (
const auto *EscapeExpr = dyn_cast<const Expr *>(EscapeTarget))
487 suggestWithScopeForImplicitThis(MD, EscapeExpr);
489 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
494 void reportFieldOriginEscapes() {
497 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
500 llvm::TimeTraceScope TimeTrace(
"ReportFieldOriginEscapes");
501 for (
auto [Field, EscapeExpr] : FieldOriginEscapesMap)
502 SemaHelper->reportFieldOriginEscape(MD, Field, EscapeExpr);
505 void reportNoescapeViolations() {
506 llvm::TimeTraceScope TimeTrace(
"ReportNoescapeViolations");
507 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
508 if (
const auto *E = dyn_cast<const Expr *>(EscapeTarget))
509 SemaHelper->reportNoescapeViolation(PVD, E);
510 else if (
const auto *FD = dyn_cast<const FieldDecl *>(EscapeTarget))
511 SemaHelper->reportNoescapeViolation(PVD, FD);
512 else if (
const auto *G = dyn_cast<const VarDecl *>(EscapeTarget))
513 SemaHelper->reportNoescapeViolation(PVD, G);
515 llvm_unreachable(
"Unhandled EscapingTarget type");
519 void reportLifetimeboundViolations() {
520 llvm::TimeTraceScope TimeTrace(
"ReportLifetimeboundViolations");
523 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
525 !VerifiedLiftimeboundEscapes.contains(MD))
526 SemaHelper->reportLifetimeboundViolation(MD);
528 if (!PVD->hasAttr<LifetimeBoundAttr>())
530 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
531 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
533 "Implicit lifetimebound parameters "
534 "should escape through return");
535 if (!isImplicit && !Escapes)
536 SemaHelper->reportLifetimeboundViolation(PVD);
542 void reportMisplacedLifetimebound() {
543 llvm::TimeTraceScope TimeTrace(
"ReportMisplacedLifetimebound");
544 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
548 auto TargetDecls = getTargetDeclsForAttr(FDef);
551 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
553 for (
auto [Decl, Scope] : TargetDecls) {
556 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
561 for (
const auto *PDef : FDef->parameters()) {
562 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
563 if (!Attr || Attr->isImplicit())
565 for (
auto [Decl, Scope] : TargetDecls) {
566 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
567 if (!PDecl->hasAttr<LifetimeBoundAttr>())
568 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
573 void reportInapplicableLifetimebound() {
574 llvm::TimeTraceScope TimeTrace(
"ReportInapplicableLifetimebound");
575 const auto *FDef = dyn_cast<FunctionDecl>(FD);
585 for (
const auto &PVD : FDef->parameters())
586 if (PVD->hasAttr<LifetimeBoundAttr>() &&
587 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
589 SemaHelper->reportInapplicableLifetimebound(PVD);
592 void inferAnnotations() {
593 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
594 if (
const auto *MD = dyn_cast<const CXXMethodDecl *>(
Target)) {
597 }
else if (
const auto *PVD = dyn_cast<const ParmVarDecl *>(
Target)) {
598 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
604 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
605 FD->getParamDecl(PVD->getFunctionScopeIndex()));
606 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
607 InferredPVD->addAttr(
608 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
618 llvm::SmallVector<const Expr *>
619 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
620 llvm::SmallVector<const Expr *> rs;
621 for (
const OriginID CurrOID : OriginFlowChain)
622 if (
const Expr *CurrExpr =
623 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
624 rs.push_back(CurrExpr);
636 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
637 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper, LSOpts);
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.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Decl - This represents one declaration (or definition), e.g.
This represents one expression.
Encodes a location in the source.
Abstract interface for operations requiring Sema access.
llvm::PointerUnion< const Expr *, const FieldDecl *, const VarDecl * > EscapingTarget
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
static bool causingFactDominatesExpiry(LivenessKind K)
utils::SetTy< LoanID > LoanSet
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const MovedLoansAnalysis &MovedLoans, const LiveOriginsAnalysis &LiveOrigins, FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper, const LifetimeSafetyOpts &LSOpts)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
utils::MapTy< OriginID, LivenessInfo > LivenessMap
const LifetimeBoundAttr * getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD)
bool isOwnerPtrCtor(const CXXConstructorDecl *Ctor, const ParmVarDecl *PVD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInStlNamespace(const Decl *D)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
bool isa(CodeGen::Address addr)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
@ TSK_ImplicitInstantiation
This template specialization was implicitly instantiated from a template.
U cast(CodeGen::Address addr)