1#include "LifetimeModeling.h"
2#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
3#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
4#include "clang/StaticAnalyzer/Core/Checker.h"
5
6using namespace clang;
7using namespace ento;
8
9namespace {
10class UseAfterLifetimeEnd : public Checker<check::EndFunction> {
11public:
12 void reportDanglingSource(const MemRegion *Source, SVal Val, ExplodedNode *N,
13 CheckerContext &C) const;
14 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
15 const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
16};
17
18class UseAfterLifetimeEndBRVisitor : public BugReporterVisitor {
19 SVal BoundVal;
20 const MemRegion *SourceRegion;
21
22public:
23 explicit UseAfterLifetimeEndBRVisitor(SVal Val, const MemRegion *Source)
24 : BoundVal(Val), SourceRegion(Source) {}
25
26 void Profile(llvm::FoldingSetNodeID &ID) const override {
27 static int X = 0;
28 ID.AddPointer(Ptr: &X);
29 BoundVal.Profile(ID);
30 SourceRegion->Profile(ID);
31 }
32
33 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
34 BugReporterContext &BRC,
35 PathSensitiveBugReport &BR) override;
36 PathDiagnosticPieceRef getEndPath(const ExplodedNode *N,
37 BugReporterContext &BRC,
38 PathSensitiveBugReport &BR) override;
39 PathDiagnosticPieceRef createSourcePiece(const ExplodedNode *N,
40 BugReporterContext &BRC,
41 StringRef Message) const;
42};
43
44} // namespace
45
46static const Expr *getLifetimeBoundArg(const Expr *RetExpr,
47 const MemRegion *Region,
48 const ExplodedNode *N) {
49 const CallExpr *Expr = dyn_cast_or_null<CallExpr>(Val: RetExpr);
50 if (!Expr)
51 return nullptr;
52
53 const FunctionDecl *FD = Expr->getDirectCallee();
54 if (!FD)
55 return nullptr;
56
57 const MemRegion *BaseReg = Region->getBaseRegion();
58
59 for (const ParmVarDecl *PVD : FD->parameters()) {
60 if (!PVD->hasAttr<LifetimeBoundAttr>())
61 continue;
62 unsigned Idx = PVD->getFunctionScopeIndex();
63
64 if (Idx >= Expr->getNumArgs())
65 continue;
66
67 const MemRegion *R = N->getSVal(E: Expr->getArg(Arg: Idx)).getAsRegion();
68 if (R && R->getBaseRegion() == BaseReg)
69 return Expr->getArg(Arg: Idx);
70 }
71 return nullptr;
72}
73
74void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
75 CheckerContext &C) const {
76 if (!RS)
77 return;
78
79 ProgramStateRef State = C.getState();
80
81 const Expr *RetExpr = RS->getRetValue();
82 if (!RetExpr)
83 return;
84
85 RetExpr = RetExpr->IgnoreParens();
86 SVal RetVal = C.getSVal(E: RetExpr);
87
88 std::vector<const MemRegion *> RetValRegion =
89 lifetime_modeling::getDanglingRegionsAfterReturn(Source: RetVal, State, C);
90 if (RetValRegion.empty())
91 return;
92
93 if (ExplodedNode *N =
94 C.generateNonFatalErrorNode(State, Pred: C.getPredecessor())) {
95 for (const MemRegion *R : RetValRegion)
96 reportDanglingSource(Source: R, Val: RetVal, N, C);
97 }
98}
99
100static SourceRange getRegionDeclRange(const MemRegion *Source) {
101 if (const auto *VR = dyn_cast_or_null<VarRegion>(Val: Source)) {
102 const VarDecl *VD = VR->getDecl();
103 return SourceRange(VD->getLocation());
104 }
105 return SourceRange();
106}
107
108void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Source,
109 SVal RetVal, ExplodedNode *N,
110 CheckerContext &C) const {
111 auto BR = std::make_unique<PathSensitiveBugReport>(
112 args: BugMsg,
113 args: (llvm::Twine("Returning value bound to ") +
114 lifetime_modeling::getRegionName(Reg: Source) + " that will go out of scope"),
115 args&: N);
116
117 if (SourceRange Range = getRegionDeclRange(Source); Range.isValid())
118 BR->addRange(R: Range);
119
120 BR->addVisitor<UseAfterLifetimeEndBRVisitor>(ConstructorArgs&: RetVal, ConstructorArgs&: Source);
121 bugreporter::trackStoredValue(V: RetVal, R: Source, Report&: *BR);
122 C.emitReport(R: std::move(BR));
123}
124
125PathDiagnosticPieceRef UseAfterLifetimeEndBRVisitor::createSourcePiece(
126 const ExplodedNode *N, BugReporterContext &BRC, StringRef Message) const {
127 const Stmt *S = N->getStmtForDiagnostics();
128 if (!S)
129 return nullptr;
130
131 const Expr *RetExpr = dyn_cast_or_null<Expr>(Val: S);
132 const Expr *Arg = getLifetimeBoundArg(RetExpr, Region: SourceRegion, N);
133
134 PathDiagnosticLocation Pos;
135
136 Pos = PathDiagnosticLocation(Arg ? Arg : S, BRC.getSourceManager(),
137 N->getStackFrame());
138
139 auto Note = std::make_shared<PathDiagnosticEventPiece>(args&: Pos, args&: Message, args: true);
140 if (SourceRange Range = getRegionDeclRange(Source: SourceRegion); Range.isValid())
141 Note->addRange(R: Range);
142
143 return Note;
144}
145
146PathDiagnosticPieceRef
147UseAfterLifetimeEndBRVisitor::VisitNode(const ExplodedNode *N,
148 BugReporterContext &BRC,
149 PathSensitiveBugReport &BR) {
150 const ExplodedNode *Pred = N->getFirstPred();
151 if (!Pred)
152 return nullptr;
153
154 if (!lifetime_modeling::isBoundToLifetimeSource(State: N->getState(), Val: BoundVal) ||
155 lifetime_modeling::isBoundToLifetimeSource(State: Pred->getState(), Val: BoundVal))
156 return nullptr;
157
158 auto Piece = createSourcePiece(
159 N, BRC,
160 Message: (llvm::Twine("Value's lifetime bound to the lifetime of ") +
161 lifetime_modeling::getRegionName(Reg: SourceRegion) + " here")
162 .str());
163 return Piece;
164}
165
166PathDiagnosticPieceRef
167UseAfterLifetimeEndBRVisitor::getEndPath(const ExplodedNode *N,
168 BugReporterContext &BRC,
169 PathSensitiveBugReport &BR) {
170 auto Piece = createSourcePiece(
171 N, BRC,
172 Message: (llvm::Twine("Lifetime of ") +
173 lifetime_modeling::getRegionName(Reg: SourceRegion) + " ended here")
174 .str());
175 return Piece;
176}
177
178void ento::registerUseAfterLifetimeEnd(CheckerManager &Mgr) {
179 Mgr.registerChecker<UseAfterLifetimeEnd>();
180}
181
182bool ento::shouldRegisterUseAfterLifetimeEnd(const CheckerManager &Mgr) {
183 return true;
184}
185