9#include "llvm/Support/raw_ostream.h"
23 :
public Checker<check::PostCall, check::DeadSymbols,
24 check::PreStmt<DeclStmt>, check::LifetimeEnd> {
26 void printState(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
27 const char *Sep)
const override;
28 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
29 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
30 void checkLifetimeEnd(
const VarDecl *VD, CheckerContext &
C)
const;
31 void checkPreStmt(
const DeclStmt *DS, CheckerContext &
C)
const;
43 if (
const auto *StackSpace =
45 const StackFrame *SF = StackSpace->getStackFrame();
51 if (llvm::any_of(
C.stackframes(), [&](
const StackFrame &Frame) {
52 return isa<CXXDestructorDecl>(Frame.getDecl());
60 if (is_contained(make_pointer_range(
C.stackframes()), SF)) {
61 if (SF == CurrentSF || !SF->
isParentOf(CurrentSF))
70 std::vector<const MemRegion *> Regions;
71 if (
auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
72 for (
const MemRegion *Region : *SourceSet) {
74 Regions.push_back(Region);
82 return State->get<LifetimeBoundMap>(Val) !=
nullptr;
87 return State->contains<DeallocatedSourceSet>(Region->
getBaseRegion());
95 return (NewState != State) ? NewState :
nullptr;
100 LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
101 const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
103 LifetimeSourceSet
Set = LSet ? *LSet : F.getEmptySet();
105 State = State->set<LifetimeBoundMap>(RetVal,
Set);
121 const auto *FC = dyn_cast<AnyFunctionCall>(&
Call);
129 SVal RetVal =
Call.getReturnValue();
132 if (PVD->hasAttr<LifetimeBoundAttr>()) {
133 unsigned Idx = PVD->getFunctionScopeIndex();
136 State =
bindSource(State, RetVal, ArgValRegion);
140 const auto *IC = dyn_cast<CXXInstanceCall>(&
Call);
142 if (
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())
143 State =
bindSource(State, RetVal, ThisRegion);
145 C.addTransition(State);
148void LifetimeModeling::checkLifetimeEnd(
const VarDecl *VD,
149 CheckerContext &
C)
const {
152 SVal SourceVal = State->getLValue(VD,
C.getStackFrame());
153 if (
const MemRegion *SourceValRegion = SourceVal.
getAsRegion()) {
154 State = State->add<DeallocatedSourceSet>(SourceValRegion);
155 C.addTransition(State);
159void LifetimeModeling::checkPreStmt(
const DeclStmt *DS,
160 CheckerContext &
C)
const {
162 for (
const auto *I : DS->
decls()) {
163 if (
const VarDecl *VD = dyn_cast<VarDecl>(I)) {
164 SVal Val = State->getLValue(VD,
C.getStackFrame());
165 if (
const MemRegion *ValRegion = Val.
getAsRegion())
166 State = State->remove<DeallocatedSourceSet>(ValRegion);
169 C.addTransition(State);
172void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
173 CheckerContext &
C)
const {
175 LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
176 DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
177 ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
179 for (SVal Val : llvm::make_first_range(LBMap)) {
187 State = State->remove<LifetimeBoundMap>(Val);
190 for (
const MemRegion *Region : Sources) {
192 State = State->remove<DeallocatedSourceSet>(Region);
195 for (
const MemRegion *Region : Reported) {
197 State = State->remove<ReportedDeadRegions>(Region);
199 C.addTransition(State);
202void LifetimeModeling::printState(raw_ostream &Out,
ProgramStateRef State,
203 const char *NL,
const char *Sep)
const {
204 auto LBMap = State->get<LifetimeBoundMap>();
205 ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
207 if (!LBMap.isEmpty()) {
208 Out << Sep <<
"LifetimeBound bindings:" << NL;
209 for (
auto &&[OriginSym, SourceSet] : LBMap) {
210 for (
const auto *Region : SourceSet)
211 Out <<
" Origin " << OriginSym <<
" contains Loan " << Region << NL;
215 if (!Reported.isEmpty()) {
216 Out << Sep <<
"Reported regions: " << NL;
217 for (
const auto *Region : Reported) {
218 Out <<
" " << Region << NL;
226class DebugLifetimeModeling :
public Checker<eval::Call> {
228 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
229 void analyzerDumpLifetimeOriginsOf(
const CallEvent &
Call,
230 CheckerContext &
C)
const;
231 const BugType BugMsg{
this,
"DebugLifetimeModeling",
"DebugLifetimeModeling"};
232 using FnCheck = void (DebugLifetimeModeling::*)(
const CallEvent &
Call,
233 CheckerContext &
C)
const;
235 const CallDescriptionMap<FnCheck> Callbacks = {
236 {{CDM::SimpleFunc, {
"clang_analyzer_dumpLifetimeOriginsOf"}},
237 &DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf},
243bool DebugLifetimeModeling::evalCall(
const CallEvent &
Call,
244 CheckerContext &
C)
const {
245 if (!isa_and_nonnull<CallExpr>(
Call.getOriginExpr()))
248 const FnCheck *Handler = Callbacks.lookup(
Call);
252 (this->*(*Handler))(
Call,
C);
256void DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf(
257 const CallEvent &
Call, CheckerContext &
C)
const {
260 if (
Call.getNumArgs() != 1) {
261 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
262 auto BR = std::make_unique<PathSensitiveBugReport>(
264 "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
266 C.emitReport(std::move(BR));
271 SVal ArgSVal =
Call.getArgSVal(0);
272 const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
277 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
278 llvm::SmallVector<std::string> RegionNames =
279 to_vector(map_range(llvm::make_pointee_range(*SourceSet),
281 llvm::sort(RegionNames);
283 llvm::SmallString<128> Str;
284 llvm::raw_svector_ostream
OS(Str);
285 OS <<
" Origin '" << ArgSVal <<
"' bound to ";
286 llvm::interleaveComma(RegionNames,
OS,
287 [&](StringRef Name) {
OS <<
"'" << Name <<
"'"; });
288 C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg,
OS.str(), N));
292void ento::registerLifetimeModeling(CheckerManager &Mgr) {
296bool ento::shouldRegisterLifetimeModeling(
const CheckerManager &Mgr) {
300void ento::registerDebugLifetimeModeling(CheckerManager &Mgr) {
304bool ento::shouldRegisterDebugLifetimeModeling(
const CheckerManager &Mgr) {
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,...
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Represents a function declaration or definition.
ArrayRef< ParmVarDecl * > parameters() const
Represents a parameter to a function.
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.
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.
MemRegion - The root abstract class for all memory regions.
std::string getDescriptiveName(bool UseQuotes=true, bool AllowFallback=false) const
Get descriptive name for memory region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
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.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
const MemRegion * getAsRegion() const
bool isLiveRegion(const MemRegion *region)
bool isLive(SymbolRef sym)
std::string getRegionName(const MemRegion *Reg)
Returns the descriptive name of the memory region or a placeholder if a descriptive name cannot be co...
bool isBoundToLifetimeSource(ProgramStateRef State, SVal Val)
Returns true if Val is a key in the LifetimeBoundMap.
bool isDeallocated(ProgramStateRef State, const MemRegion *Region)
Returns true if the underlying MemRegion is deallocated.
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.
ProgramStateRef markAsReported(ProgramStateRef State, const MemRegion *Region)
Returns the updated State with R marked as reported if R is seen the first time.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
Top level wrappers for InstallAPI frontend operations.