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;
73 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
74 if (
const auto *UF = F.dyn_cast<
const UseFact *>())
75 return UF->getUseExpr()->getExprLoc();
76 if (
const auto *OEF = F.dyn_cast<
const OriginEscapesFact *>()) {
77 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
78 return ReturnEsc->getReturnExpr()->getExprLoc();
79 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
80 return FieldEsc->getFieldDecl()->getLocation();
82 llvm_unreachable(
"unhandled causing fact in PointerUnion");
86 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
87 const MovedLoansAnalysis &MovedLoans,
88 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
89 AnalysisDeclContext &ADC,
90 LifetimeSafetySemaHelper *SemaHelper)
91 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
92 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
93 AST(ADC.getASTContext()), FD(ADC.getDecl()) {
94 for (
const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
95 for (
const Fact *F : FactMgr.getFacts(B))
96 if (
const auto *EF = F->getAs<ExpireFact>())
98 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
99 checkInvalidation(IOF);
100 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
101 checkAnnotations(OEF);
102 issuePendingWarnings();
103 suggestAnnotations();
104 reportNoescapeViolations();
105 reportLifetimeboundViolations();
109 if (AST.getLangOpts().EnableLifetimeSafetyInference)
116 void checkAnnotations(
const OriginEscapesFact *OEF) {
117 OriginID EscapedOID = OEF->getEscapedOriginID();
118 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
119 auto CheckParam = [&](
const ParmVarDecl *PVD,
bool IsMoved) {
121 if (PVD->hasAttr<NoEscapeAttr>()) {
122 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
123 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
124 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
125 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
126 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
127 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
134 if (PVD->hasAttr<LifetimeBoundAttr>()) {
136 VerifiedLiftimeboundEscapes.insert(PVD);
140 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
141 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
142 else if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
144 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
149 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
151 VerifiedLiftimeboundEscapes.insert(MD);
152 else if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
153 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
155 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
156 for (
LoanID LID : EscapedLoans) {
157 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
158 const AccessPath &AP = L->getAccessPath();
159 if (
const auto *PVD = AP.getAsPlaceholderParam())
160 CheckParam(PVD, MovedAtEscape.lookup(LID));
161 else if (
const auto *MD = AP.getAsPlaceholderThis())
162 CheckImplicitThis(MD);
172 void checkExpiry(
const ExpireFact *EF) {
173 const AccessPath &ExpiredPath = EF->getAccessPath();
174 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
175 for (
auto &[OID, LiveInfo] : Origins) {
176 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
177 for (
LoanID HeldLoanID : HeldLoans) {
178 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
179 if (ExpiredPath != HeldLoan->getAccessPath())
182 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
183 const Expr *MovedExpr =
nullptr;
184 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
187 if (CurWarning.CausingFactDominatesExpiry)
190 CurWarning.CausingFactDominatesExpiry =
true;
191 CurWarning.CausingFact = LiveInfo.CausingFact;
192 CurWarning.ExpiryLoc = EF->getExpiryLoc();
193 CurWarning.MovedExpr = MovedExpr;
194 CurWarning.InvalidatedByExpr =
nullptr;
204 void checkInvalidation(
const InvalidateOriginFact *IOF) {
205 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
207 LoanSet DirectlyInvalidatedLoans =
208 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
209 auto IsInvalidated = [&](
const Loan *L) {
210 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
211 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
212 if (InvalidL->getAccessPath() == L->getAccessPath())
218 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(IOF);
219 for (
auto &[OID, LiveInfo] : Origins) {
220 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
221 for (
LoanID LiveLoanID : HeldLoans)
222 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
224 bool LastDomination =
225 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
226 if (!LastDomination) {
227 FinalWarningsMap[LiveLoanID] = {
229 LiveInfo.CausingFact,
231 IOF->getInvalidationExpr(),
238 void issuePendingWarnings() {
241 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
242 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
243 const Expr *IssueExpr = L->getIssuingExpr();
244 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
245 CausingFact =
Warning.CausingFact;
246 const ParmVarDecl *InvalidatedPVD =
247 L->getAccessPath().getAsPlaceholderParam();
248 const Expr *MovedExpr =
Warning.MovedExpr;
249 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
251 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
252 if (
Warning.InvalidatedByExpr) {
255 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
257 else if (InvalidatedPVD)
259 SemaHelper->reportUseAfterInvalidation(
260 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
264 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
265 MovedExpr, ExpiryLoc);
266 }
else if (
const auto *OEF =
267 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
268 if (
Warning.InvalidatedByExpr) {
269 if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
273 SemaHelper->reportInvalidatedField(IssueExpr,
274 FieldEscape->getFieldDecl(),
276 else if (InvalidatedPVD)
278 SemaHelper->reportInvalidatedField(InvalidatedPVD,
279 FieldEscape->getFieldDecl(),
281 }
else if (
const auto *GlobalEscape =
282 dyn_cast<GlobalEscapeFact>(OEF)) {
287 SemaHelper->reportInvalidatedGlobal(IssueExpr,
288 GlobalEscape->getGlobal(),
290 else if (InvalidatedPVD)
292 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
293 GlobalEscape->getGlobal(),
298 llvm_unreachable(
"Unhandled OriginEscapesFact type");
299 }
else if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
301 SemaHelper->reportUseAfterReturn(
302 IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
303 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
305 SemaHelper->reportDanglingField(
306 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
307 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
309 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
310 MovedExpr, ExpiryLoc);
312 llvm_unreachable(
"Unhandled OriginEscapesFact type");
314 llvm_unreachable(
"Unhandled CausingFact type");
320 static const FunctionDecl *getCrossTUDecl(
const FunctionDecl &FD,
322 if (!FD.isExternallyVisible())
324 const FileID DefinitionFile =
SM.getFileID(FD.getLocation());
325 for (
const FunctionDecl *Redecl : FD.redecls())
326 if (
SM.getFileID(Redecl->getLocation()) != DefinitionFile)
332 static const FunctionDecl *getCrossTUDecl(
const ParmVarDecl &PVD,
334 if (
const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
335 return getCrossTUDecl(*FD,
SM);
339 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
340 const ParmVarDecl *PVD,
342 EscapingTarget EscapeTarget) {
343 if (llvm::isa<const VarDecl *>(EscapeTarget))
346 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD,
SM))
347 SemaHelper->suggestLifetimeboundToParmVar(
349 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
357 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
358 const CXXMethodDecl *MD, SourceManager &
SM,
359 const Expr *EscapeExpr) {
360 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD,
SM))
361 SemaHelper->suggestLifetimeboundToImplicitThis(
369 void suggestAnnotations() {
372 SourceManager &
SM = AST.getSourceManager();
373 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
374 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
375 suggestWithScopeForParmVar(SemaHelper, PVD,
SM, EscapeTarget);
376 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
377 if (
const auto *EscapeExpr = EscapeTarget.dyn_cast<
const Expr *>())
378 suggestWithScopeForImplicitThis(SemaHelper, MD,
SM, EscapeExpr);
380 llvm_unreachable(
"Implicit this can only escape via Expr (return)");
385 void reportNoescapeViolations() {
386 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
387 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
388 SemaHelper->reportNoescapeViolation(PVD, E);
389 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
390 SemaHelper->reportNoescapeViolation(PVD, FD);
391 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
392 SemaHelper->reportNoescapeViolation(PVD, G);
394 llvm_unreachable(
"Unhandled EscapingTarget type");
398 void reportLifetimeboundViolations() {
401 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
403 !VerifiedLiftimeboundEscapes.contains(MD))
404 SemaHelper->reportLifetimeboundViolation(MD);
406 if (!PVD->hasAttr<LifetimeBoundAttr>())
408 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
409 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
411 "Implicit lifetimebound parameters "
412 "should escape through return");
413 if (!isImplicit && !Escapes)
414 SemaHelper->reportLifetimeboundViolation(PVD);
418 void inferAnnotations() {
419 for (
auto [
Target, EscapeTarget] : AnnotationWarningsMap) {
420 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
423 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
424 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
430 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
431 FD->getParamDecl(PVD->getFunctionScopeIndex()));
432 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
433 InferredPVD->addAttr(
434 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
446 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
447 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.
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
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 LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInStlNamespace(const Decl *D)
bool isa(CodeGen::Address addr)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
U cast(CodeGen::Address addr)