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::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
64 const LoanPropagationAnalysis &LoanPropagation;
65 const MovedLoansAnalysis &MovedLoans;
66 const LiveOriginsAnalysis &LiveOrigins;
68 LifetimeSafetySemaHelper *SemaHelper;
72 const LifetimeSafetyOpts &LSOpts;
75 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
76 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
77 return UF->getUseExpr()->getExprLoc();
78 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
79 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
80 return ReturnEsc->getReturnExpr()->getExprLoc();
81 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
82 return FieldEsc->getFieldDecl()->getLocation();
84 llvm_unreachable(
"unhandled causing fact in PointerUnion");
88 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
89 const MovedLoansAnalysis &MovedLoans,
90 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
91 AnalysisDeclContext &ADC,
92 LifetimeSafetySemaHelper *SemaHelper,
93 const LifetimeSafetyOpts &LSOpts)
94 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
95 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
96 AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
98 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
99 for (
const Fact *F : FactMgr.getFacts(B))
100 if (
const auto *EF = F->getAs<ExpireFact>())
102 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
103 checkInvalidation(IOF);
104 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
105 checkAnnotations(OEF);
106 issuePendingWarnings();
107 suggestAnnotations();
108 if (LSOpts.CheckNoescapeViolations)
109 reportNoescapeViolations();
110 if (LSOpts.CheckLifetimeboundViolations)
111 reportLifetimeboundViolations();
112 if (LSOpts.CheckMisplacedLifetimebound)
113 reportMisplacedLifetimebound();
114 if (LSOpts.CheckInapplicableLifetimebound)
115 reportInapplicableLifetimebound();
119 if (AST.getLangOpts().EnableLifetimeSafetyInference)
126 void checkAnnotations(
const OriginEscapesFact *OEF) {
127 OriginID EscapedOID = OEF->getEscapedOriginID();
128 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
129 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
131 if (PVD->hasAttr<NoEscapeAttr>()) {
132 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
133 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
134 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
135 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
136 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
137 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
144 if (PVD->hasAttr<LifetimeBoundAttr>()) {
149 VerifiedLiftimeboundEscapes.insert(PVD);
153 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
154 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
155 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
157 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
162 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
163 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
165 VerifiedLiftimeboundEscapes.insert(MD);
167 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
170 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
171 for (
LoanID LID : EscapedLoans) {
172 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
173 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
176 if (
const auto *PVD = PB->getParmVarDecl())
177 CheckParam(PVD, MovedAtEscape.lookup(LID));
178 else if (
const auto *MD = PB->getImplicitThisParent())
179 CheckImplicitThis(MD);
190 void checkExpiry(
const ExpireFact *EF) {
191 const AccessPath &ExpiredPath = EF->getAccessPath();
192 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
193 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
194 for (
auto &[OID, LiveInfo] : Live) {
195 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
196 for (
LoanID HeldLoanID : HeldLoans) {
197 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
198 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
201 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
202 const Expr *MovedExpr =
nullptr;
203 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
206 if (CurWarning.CausingFactDominatesExpiry)
209 CurWarning.CausingFactDominatesExpiry =
true;
210 CurWarning.CausingFact = LiveInfo.CausingFact;
211 CurWarning.ExpiryLoc = EF->getExpiryLoc();
212 CurWarning.MovedExpr = MovedExpr;
213 CurWarning.InvalidatedByExpr =
nullptr;
225 void checkInvalidation(
const InvalidateOriginFact *IOF) {
226 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
228 LoanSet DirectlyInvalidatedLoans =
229 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
230 auto IsInvalidated = [&](
const Loan *L) {
231 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
232 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
233 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
239 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
240 for (
const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
241 for (
auto &[OID, LiveInfo] : Live) {
242 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
243 for (
LoanID LiveLoanID : HeldLoans)
244 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
246 bool LastDomination =
247 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
248 if (!LastDomination) {
249 FinalWarningsMap[LiveLoanID] = {
251 LiveInfo.CausingFact,
253 IOF->getInvalidationExpr(),
260 void issuePendingWarnings() {
261 llvm::TimeTraceScope TimeTrace(
"IssuePendingWarnings");
264 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
265 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
266 const Expr *IssueExpr = L->getIssueExpr();
267 const ParmVarDecl *InvalidatedPVD =
nullptr;
268 if (
const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
269 InvalidatedPVD = PB->getParmVarDecl();
271 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
272 CausingFact =
Warning.CausingFact;
273 const Expr *MovedExpr =
Warning.MovedExpr;
274 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
276 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
277 llvm::SmallVector<const Expr *> ExprChain =
278 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
279 if (
Warning.InvalidatedByExpr) {
282 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
285 else if (InvalidatedPVD)
287 SemaHelper->reportUseAfterInvalidation(
288 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr,
293 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
294 MovedExpr, ExpiryLoc, ExprChain);
296 }
else if (
const auto *OEF =
297 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
298 if (
Warning.InvalidatedByExpr) {
299 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
303 SemaHelper->reportInvalidatedField(IssueExpr,
304 FieldEscape->getFieldDecl(),
306 else if (InvalidatedPVD)
308 SemaHelper->reportInvalidatedField(InvalidatedPVD,
309 FieldEscape->getFieldDecl(),
311 }
else if (
const auto *GlobalEscape =
312 dyn_cast<GlobalEscapeFact>(OEF)) {
317 SemaHelper->reportInvalidatedGlobal(IssueExpr,
318 GlobalEscape->getGlobal(),
320 else if (InvalidatedPVD)
322 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
323 GlobalEscape->getGlobal(),
328 llvm_unreachable(
"Unhandled OriginEscapesFact type");
329 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
331 SemaHelper->reportUseAfterReturn(
332 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
333 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
335 bool IsCapturedByLambda =
336 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
337 SemaHelper->reportDanglingField(
338 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
339 IsCapturedByLambda, ExpiryLoc);
340 }
else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
343 if (
const auto *
Func = dyn_cast_if_present<FunctionDecl>(FD))
344 IsMain =
Func->isMain();
345 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
346 MovedExpr, ExpiryLoc, IsMain);
348 llvm_unreachable(
"Unhandled OriginEscapesFact type");
350 llvm_unreachable(
"Unhandled CausingFact type");
359 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
360 getTargetDeclsForAttr(
const FunctionDecl *FDef) {
364 assert(FDef->isThisDeclarationADefinition() &&
365 "Expected FunctionDecl to be a definition");
367 const auto &SM = FDef->getASTContext().getSourceManager();
369 auto GetFile = [&SM](
const FunctionDecl *FD) {
370 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
373 const FileID DefFile = GetFile(FDef);
374 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
375 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
376 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
382 auto AddCrossTUDecl = [&](
const FunctionDecl *FD) {
383 FileID
File = GetFile(FD);
386 for (
auto [SeenFD, _] : Targets)
387 if (GetFile(SeenFD) ==
File)
396 auto redecls = llvm::to_vector(FDef->redecls());
398 for (
const FunctionDecl *Redecl : llvm::reverse(redecls))
399 AddCrossTUDecl(Redecl);
404 void suggestWithScopeForParmVar(
const ParmVarDecl *PVD,
405 EscapingTarget EscapeTarget) {
406 if (llvm::isa<const VarDecl *>(EscapeTarget))
410 const auto *ParmToAnnotate =
411 Decl->getParamDecl(PVD->getFunctionScopeIndex());
412 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
417 void suggestWithScopeForImplicitThis(
const CXXMethodDecl *MD,
418 const Expr *EscapeExpr) {
419 for (
auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
420 SemaHelper->suggestLifetimeboundToImplicitThis(
425 void suggestAnnotations() {
428 if (!LSOpts.SuggestAnnotations)
430 llvm::TimeTraceScope TimeTrace(
"SuggestAnnotations");
431 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
432 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
433 suggestWithScopeForParmVar(PVD, EscapeTarget);
434 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
435 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
436 suggestWithScopeForImplicitThis(MD, EscapeExpr);
438 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
443 void reportNoescapeViolations() {
444 llvm::TimeTraceScope TimeTrace(
"ReportNoescapeViolations");
445 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
446 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
447 SemaHelper->reportNoescapeViolation(PVD, E);
448 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
449 SemaHelper->reportNoescapeViolation(PVD, FD);
450 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
451 SemaHelper->reportNoescapeViolation(PVD, G);
453 llvm_unreachable(
"Unhandled EscapingTarget type");
457 void reportLifetimeboundViolations() {
458 llvm::TimeTraceScope TimeTrace(
"ReportLifetimeboundViolations");
461 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
463 !VerifiedLiftimeboundEscapes.contains(MD))
464 SemaHelper->reportLifetimeboundViolation(MD);
466 if (!PVD->hasAttr<LifetimeBoundAttr>())
468 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
469 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
471 "Implicit lifetimebound parameters "
472 "should escape through return");
473 if (!isImplicit && !Escapes)
474 SemaHelper->reportLifetimeboundViolation(PVD);
480 void reportMisplacedLifetimebound() {
481 llvm::TimeTraceScope TimeTrace(
"ReportMisplacedLifetimebound");
482 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
486 auto TargetDecls = getTargetDeclsForAttr(FDef);
489 if (
const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
491 for (
auto [Decl, Scope] : TargetDecls) {
494 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
499 for (
const auto *PDef : FDef->parameters()) {
500 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
501 if (!Attr || Attr->isImplicit())
503 for (
auto [Decl, Scope] : TargetDecls) {
504 const auto *PDecl =
Decl->getParamDecl(PDef->getFunctionScopeIndex());
505 if (!PDecl->hasAttr<LifetimeBoundAttr>())
506 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
511 void reportInapplicableLifetimebound() {
512 llvm::TimeTraceScope TimeTrace(
"ReportInapplicableLifetimebound");
513 const auto *FDef = dyn_cast<FunctionDecl>(FD);
523 for (
const auto &PVD : FDef->parameters())
524 if (PVD->hasAttr<LifetimeBoundAttr>() &&
525 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
527 SemaHelper->reportInapplicableLifetimebound(PVD);
530 void inferAnnotations() {
531 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
532 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
535 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
536 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
542 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
543 FD->getParamDecl(PVD->getFunctionScopeIndex()));
544 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
545 InferredPVD->addAttr(
546 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
556 llvm::SmallVector<const Expr *>
557 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
558 llvm::SmallVector<const Expr *> rs;
559 for (
const OriginID CurrOID : OriginFlowChain)
560 if (
const Expr *CurrExpr =
561 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
562 rs.push_back(CurrExpr);
574 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
575 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 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)