26#include "llvm/ADT/DenseMap.h"
27#include "llvm/ADT/DenseSet.h"
28#include "llvm/Support/Casting.h"
29#include "llvm/Support/ErrorHandling.h"
30#include "llvm/Support/TimeProfiler.h"
43 llvm_unreachable(
"unknown liveness kind");
49struct PendingWarning {
51 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
52 const Expr *MovedExpr;
53 const Expr *InvalidatedByExpr;
57using AnnotationTarget =
58 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
60 llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
62class LifetimeChecker {
64 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
65 llvm::DenseMap<AnnotationTarget, const Expr *> AnnotationWarningsMap;
66 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
67 const LoanPropagationAnalysis &LoanPropagation;
68 const MovedLoansAnalysis &MovedLoans;
69 const LiveOriginsAnalysis &LiveOrigins;
71 LifetimeSafetySemaHelper *SemaHelper;
75 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
76 const MovedLoansAnalysis &MovedLoans,
77 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
79 LifetimeSafetySemaHelper *SemaHelper)
80 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
81 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
82 AST(ADC.getASTContext()) {
84 for (
const Fact *F : FactMgr.getFacts(B))
85 if (
const auto *EF = F->getAs<ExpireFact>())
87 else if (
const auto *IOF = F->getAs<InvalidateOriginFact>())
88 checkInvalidation(IOF);
89 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
90 checkAnnotations(OEF);
91 issuePendingWarnings();
93 reportNoescapeViolations();
97 if (AST.
getLangOpts().EnableLifetimeSafetyInference)
104 void checkAnnotations(
const OriginEscapesFact *OEF) {
105 OriginID EscapedOID = OEF->getEscapedOriginID();
106 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
107 auto CheckParam = [&](
const ParmVarDecl *PVD) {
109 if (PVD->hasAttr<NoEscapeAttr>()) {
110 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
111 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
112 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
113 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
114 if (
auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
115 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
119 if (!PVD->hasAttr<LifetimeBoundAttr>())
120 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
121 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
125 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
127 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
128 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
130 for (
LoanID LID : EscapedLoans) {
131 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
132 const auto *PL = dyn_cast<PlaceholderLoan>(L);
135 if (
const auto *PVD = PL->getParmVarDecl())
137 else if (
const auto *MD = PL->getMethodDecl())
138 CheckImplicitThis(MD);
153 void checkExpiry(
const ExpireFact *EF) {
154 LoanID ExpiredLoan = EF->getLoanID();
155 const Expr *MovedExpr =
nullptr;
156 if (
auto *ME = MovedLoans.getMovedLoans(EF).lookup(ExpiredLoan))
159 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
163 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
164 BestCausingFact =
nullptr;
166 for (
auto &[OID, LiveInfo] : Origins) {
167 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
168 if (!HeldLoans.contains(ExpiredLoan))
172 if (CurConfidence < NewConfidence) {
173 CurConfidence = NewConfidence;
174 BestCausingFact = LiveInfo.CausingFact;
177 if (!BestCausingFact)
180 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
181 if (LastConf >= CurConfidence)
183 FinalWarningsMap[ExpiredLoan] = {EF->getExpiryLoc(),
195 void checkInvalidation(
const InvalidateOriginFact *IOF) {
196 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
198 LoanSet DirectlyInvalidatedLoans =
199 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
200 auto IsInvalidated = [&](
const Loan *L) {
201 auto *PathL = dyn_cast<PathLoan>(L);
202 auto *PlaceholderL = dyn_cast<PlaceholderLoan>(L);
203 for (
LoanID InvalidID : DirectlyInvalidatedLoans) {
204 const Loan *L = FactMgr.getLoanMgr().getLoan(InvalidID);
205 auto *InvalidPathL = dyn_cast<PathLoan>(L);
206 auto *InvalidPlaceholderL = dyn_cast<PlaceholderLoan>(L);
207 if (PathL && InvalidPathL &&
208 PathL->getAccessPath() == InvalidPathL->getAccessPath())
210 if (PlaceholderL && InvalidPlaceholderL &&
211 PlaceholderL->getParmVarDecl() ==
212 InvalidPlaceholderL->getParmVarDecl())
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))) {
225 FinalWarningsMap.lookup(LiveLoanID).ConfidenceLevel;
226 if (LastConf < CurConfidence) {
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);
244 const Expr *IssueExpr =
nullptr;
245 if (
const auto *BL = dyn_cast<PathLoan>(L))
246 IssueExpr = BL->getIssueExpr();
247 const ParmVarDecl *InvalidatedPVD =
nullptr;
248 if (
const auto *PL = dyn_cast<PlaceholderLoan>(L))
249 InvalidatedPVD = PL->getParmVarDecl();
250 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
251 CausingFact =
Warning.CausingFact;
253 const Expr *MovedExpr =
Warning.MovedExpr;
254 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
256 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>()) {
257 if (
Warning.InvalidatedByExpr) {
259 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
262 SemaHelper->reportUseAfterInvalidation(
263 InvalidatedPVD, UF->getUseExpr(),
Warning.InvalidatedByExpr);
266 SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
268 }
else if (
const auto *OEF =
269 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
270 if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
271 SemaHelper->reportUseAfterReturn(IssueExpr,
272 RetEscape->getReturnExpr(),
274 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
275 SemaHelper->reportDanglingField(
276 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
277 else if (
const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
278 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
279 MovedExpr, ExpiryLoc);
281 llvm_unreachable(
"Unhandled OriginEscapesFact type");
283 llvm_unreachable(
"Unhandled CausingFact type");
289 static const FunctionDecl *getCrossTUDecl(
const FunctionDecl &FD,
291 if (!FD.isExternallyVisible())
293 const FileID DefinitionFile =
SM.getFileID(FD.getLocation());
294 for (
const FunctionDecl *Redecl : FD.redecls())
295 if (
SM.getFileID(Redecl->getLocation()) != DefinitionFile)
301 static const FunctionDecl *getCrossTUDecl(
const ParmVarDecl &PVD,
303 if (
const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
304 return getCrossTUDecl(*FD,
SM);
308 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
309 const ParmVarDecl *PVD,
311 const Expr *EscapeExpr) {
312 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD,
SM))
313 SemaHelper->suggestLifetimeboundToParmVar(
315 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr);
322 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
323 const CXXMethodDecl *MD, SourceManager &
SM,
324 const Expr *EscapeExpr) {
325 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD,
SM))
326 SemaHelper->suggestLifetimeboundToImplicitThis(
334 void suggestAnnotations() {
337 SourceManager &
SM = AST.getSourceManager();
338 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
339 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
340 suggestWithScopeForParmVar(SemaHelper, PVD,
SM, EscapeExpr);
341 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>())
342 suggestWithScopeForImplicitThis(SemaHelper, MD,
SM, EscapeExpr);
346 void reportNoescapeViolations() {
347 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
348 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
349 SemaHelper->reportNoescapeViolation(PVD, E);
350 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
351 SemaHelper->reportNoescapeViolation(PVD, FD);
352 else if (
const auto *G = EscapeTarget.dyn_cast<
const VarDecl *>())
353 SemaHelper->reportNoescapeViolation(PVD, G);
355 llvm_unreachable(
"Unhandled EscapingTarget type");
359 void inferAnnotations() {
360 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
361 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
364 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
365 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
371 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
372 FD->getParamDecl(PVD->getFunctionScopeIndex()));
373 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
374 InferredPVD->addAttr(
375 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
387 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
388 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 ...
const LangOptions & getLangOpts() const
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a single basic block in a source-level CFG.
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
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...
static Confidence livenessKindToConfidence(LivenessKind K)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
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)