clang 23.0.0git
LifetimeModeling.cpp
Go to the documentation of this file.
1#include "LifetimeModeling.h"
2#include "clang/AST/Attr.h"
9#include "llvm/Support/raw_ostream.h"
10
11using namespace clang;
12using namespace ento;
13
14REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
15REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
16
17namespace {
18
19class LifetimeModeling : public Checker<check::PostCall, check::DeadSymbols> {
20public:
21 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
22 const char *Sep) const override;
23 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
24 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
25};
26
27} // namespace
28
29static bool isDanglingStackSource(const MemRegion *Source,
31 // FIXME: The checker currently handles stack-region sources. Other
32 // region kinds require separate methodology. For example, heap
33 // regions do not go out of scope at the end of a stack frame, so
34 // in order to detect those type of dangling sources the function
35 // needs to be expanded to an event-driven approach as well.
36 if (const auto *StackSpace =
37 Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
38 const StackFrame *SF = StackSpace->getStackFrame();
39 const StackFrame *CurrentSF = C.getStackFrame();
40 if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
41 return true;
42 }
43 return false;
44}
45
47 SVal Val, ProgramStateRef State, CheckerContext &C) {
48 std::vector<const MemRegion *> Regions;
49 if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
50 for (const MemRegion *Region : *SourceSet) {
51 if (isDanglingStackSource(Region, State, C))
52 Regions.push_back(Region);
53 }
54 }
55 return Regions;
56}
57
59 const MemRegion *Source) {
60 LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
61 const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
62
63 LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
64 Set = F.add(Set, Source);
65 State = State->set<LifetimeBoundMap>(RetVal, Set);
66 return State;
67}
68
69void LifetimeModeling::checkPostCall(const CallEvent &Call,
70 CheckerContext &C) const {
71 ProgramStateRef State = C.getState();
72
73 const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
74 if (!FC)
75 return;
76
77 const FunctionDecl *FD = FC->getDecl();
78 if (!FD)
79 return;
80
81 SVal RetVal = Call.getReturnValue();
82
83 for (const ParmVarDecl *PVD : FD->parameters()) {
84 if (PVD->hasAttr<LifetimeBoundAttr>()) {
85 unsigned Idx = PVD->getFunctionScopeIndex();
86 SVal Arg = Call.getArgSVal(Idx);
87 if (const MemRegion *ArgValRegion = Arg.getAsRegion())
88 State = bindSource(State, RetVal, ArgValRegion);
89 }
90 }
91
92 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
94 if (const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())
95 State = bindSource(State, RetVal, ThisRegion);
96 }
97 C.addTransition(State);
98}
99
100void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
101 CheckerContext &C) const {
102 ProgramStateRef State = C.getState();
103 LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
104
105 for (SVal Val : llvm::make_first_range(LBMap)) {
106 if (const auto *R = Val.getAsRegion(); R && SymReaper.isLiveRegion(R))
107 continue;
108
109 if (SymbolRef S = Val.getAsSymbol(/*IncludeBaseRegions=*/true);
110 S && SymReaper.isLive(S))
111 continue;
112
113 State = State->remove<LifetimeBoundMap>(Val);
114 }
115 C.addTransition(State);
116}
117
118void LifetimeModeling::printState(raw_ostream &Out, ProgramStateRef State,
119 const char *NL, const char *Sep) const {
120 auto LBMap = State->get<LifetimeBoundMap>();
121
122 if (LBMap.isEmpty())
123 return;
124
125 Out << Sep << "LifetimeBound bindings:" << NL;
126 for (auto &&[OriginSym, SourceSet] : LBMap) {
127 for (const auto *Region : SourceSet)
128 Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
129 }
130}
131
132// FIXME: Eventually move the debug checker to its own source file once
133// it has more functionality.
134namespace {
135class DebugLifetimeModeling : public Checker<eval::Call> {
136public:
137 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
138 void analyzerDumpLifetimeOriginsOf(const CallEvent &Call,
139 CheckerContext &C) const;
140 const BugType BugMsg{this, "DebugLifetimeModeling", "DebugLifetimeModeling"};
141 using FnCheck = void (DebugLifetimeModeling::*)(const CallEvent &Call,
142 CheckerContext &C) const;
143
144 const CallDescriptionMap<FnCheck> Callbacks = {
145 {{CDM::SimpleFunc, {"clang_analyzer_dumpLifetimeOriginsOf"}},
146 &DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf},
147 };
148};
149
150} // namespace
151
152bool DebugLifetimeModeling::evalCall(const CallEvent &Call,
153 CheckerContext &C) const {
154 if (!isa_and_nonnull<CallExpr>(Call.getOriginExpr()))
155 return false;
156
157 const FnCheck *Handler = Callbacks.lookup(Call);
158 if (!Handler)
159 return false;
160
161 (this->*(*Handler))(Call, C);
162 return true;
163}
164
165void DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf(
166 const CallEvent &Call, CheckerContext &C) const {
167 ProgramStateRef State = C.getState();
168
169 if (Call.getNumArgs() != 1) {
170 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
171 auto BR = std::make_unique<PathSensitiveBugReport>(
172 BugMsg,
173 "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
174 N);
175 C.emitReport(std::move(BR));
176 }
177 return;
178 }
179
180 SVal ArgSVal = Call.getArgSVal(0);
181 const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
182
183 if (!SourceSet)
184 return;
185
186 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
187 llvm::SmallVector<std::string> RegionNames =
188 to_vector(map_range(llvm::make_pointee_range(*SourceSet),
189 std::mem_fn(&MemRegion::getString)));
190 llvm::sort(RegionNames);
191
192 llvm::SmallString<128> Str;
193 llvm::raw_svector_ostream OS(Str);
194 OS << " Origin " << ArgSVal << " bound to ";
195 llvm::interleaveComma(RegionNames, OS);
196 C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), N));
197 }
198}
199
200void ento::registerLifetimeModeling(CheckerManager &Mgr) {
201 Mgr.registerChecker<LifetimeModeling>();
202}
203
204bool ento::shouldRegisterLifetimeModeling(const CheckerManager &Mgr) {
205 return true;
206}
207
208void ento::registerDebugLifetimeModeling(CheckerManager &Mgr) {
209 Mgr.registerChecker<DebugLifetimeModeling>();
210}
211
212bool ento::shouldRegisterDebugLifetimeModeling(const CheckerManager &Mgr) {
213 return true;
214}
static bool isDanglingStackSource(const MemRegion *Source, ProgramStateRef State, CheckerContext &C)
static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal, const MemRegion *Source)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set type Name and registers the factory for such sets in the program state,...
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2814
It represents a stack frame of the call stack.
bool isParentOf(const StackFrame *SF) const
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:152
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
std::string getString() const
Get a string representation of a region for debug use.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:57
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
bool isLiveRegion(const MemRegion *region)
bool isLive(SymbolRef sym)
std::vector< const MemRegion * > getDanglingRegionsAfterReturn(SVal Source, ProgramStateRef State, CheckerContext &C)
Returns the set of lifetime sources bound to Source that are dangling stack regions.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
The JSON file list parser is used to communicate input to InstallAPI.