9#include "llvm/Support/raw_ostream.h"
22 :
public Checker<check::PostCall, check::DeadSymbols,
23 check::PreStmt<DeclStmt>, check::LifetimeEnd> {
25 void printState(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
26 const char *Sep)
const override;
27 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
28 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
29 void checkLifetimeEnd(
const VarDecl *VD, CheckerContext &
C)
const;
30 void checkPreStmt(
const DeclStmt *DS, CheckerContext &
C)
const;
42 if (
const auto *StackSpace =
44 const StackFrame *SF = StackSpace->getStackFrame();
50 if (llvm::any_of(
C.stackframes(), [&](
const StackFrame &Frame) {
51 return isa<CXXDestructorDecl>(Frame.getDecl());
56 if (SF == CurrentSF || !SF->
isParentOf(CurrentSF))
64 std::vector<const MemRegion *> Regions;
65 if (
auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
66 for (
const MemRegion *Region : *SourceSet) {
68 Regions.push_back(Region);
76 return State->contains<DeallocatedSourceSet>(Region->
getBaseRegion());
81 LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
82 const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
84 LifetimeSourceSet
Set = LSet ? *LSet : F.getEmptySet();
86 State = State->set<LifetimeBoundMap>(RetVal,
Set);
102 const auto *FC = dyn_cast<AnyFunctionCall>(&
Call);
110 SVal RetVal =
Call.getReturnValue();
113 if (PVD->hasAttr<LifetimeBoundAttr>()) {
114 unsigned Idx = PVD->getFunctionScopeIndex();
117 State =
bindSource(State, RetVal, ArgValRegion);
121 const auto *IC = dyn_cast<CXXInstanceCall>(&
Call);
123 if (
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())
124 State =
bindSource(State, RetVal, ThisRegion);
126 C.addTransition(State);
129void LifetimeModeling::checkLifetimeEnd(
const VarDecl *VD,
130 CheckerContext &
C)
const {
133 SVal SourceVal = State->getLValue(VD,
C.getStackFrame());
134 if (
const MemRegion *SourceValRegion = SourceVal.
getAsRegion()) {
135 State = State->add<DeallocatedSourceSet>(SourceValRegion);
136 C.addTransition(State);
140void LifetimeModeling::checkPreStmt(
const DeclStmt *DS,
141 CheckerContext &
C)
const {
143 for (
const auto *I : DS->
decls()) {
144 if (
const VarDecl *VD = dyn_cast<VarDecl>(I)) {
145 SVal Val = State->getLValue(VD,
C.getStackFrame());
146 if (
const MemRegion *ValRegion = Val.
getAsRegion())
147 State = State->remove<DeallocatedSourceSet>(ValRegion);
150 C.addTransition(State);
153void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
154 CheckerContext &
C)
const {
156 LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
157 DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
159 for (SVal Val : llvm::make_first_range(LBMap)) {
167 State = State->remove<LifetimeBoundMap>(Val);
170 for (
const MemRegion *Region : Sources) {
172 State = State->remove<DeallocatedSourceSet>(Region);
174 C.addTransition(State);
177void LifetimeModeling::printState(raw_ostream &Out,
ProgramStateRef State,
178 const char *NL,
const char *Sep)
const {
179 auto LBMap = State->get<LifetimeBoundMap>();
184 Out << Sep <<
"LifetimeBound bindings:" << NL;
185 for (
auto &&[OriginSym, SourceSet] : LBMap) {
186 for (
const auto *Region : SourceSet)
187 Out <<
" Origin " << OriginSym <<
" contains Loan " << Region << NL;
194class DebugLifetimeModeling :
public Checker<eval::Call> {
196 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
197 void analyzerDumpLifetimeOriginsOf(
const CallEvent &
Call,
198 CheckerContext &
C)
const;
199 const BugType BugMsg{
this,
"DebugLifetimeModeling",
"DebugLifetimeModeling"};
200 using FnCheck = void (DebugLifetimeModeling::*)(
const CallEvent &
Call,
201 CheckerContext &
C)
const;
203 const CallDescriptionMap<FnCheck> Callbacks = {
204 {{CDM::SimpleFunc, {
"clang_analyzer_dumpLifetimeOriginsOf"}},
205 &DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf},
211bool DebugLifetimeModeling::evalCall(
const CallEvent &
Call,
212 CheckerContext &
C)
const {
213 if (!isa_and_nonnull<CallExpr>(
Call.getOriginExpr()))
216 const FnCheck *Handler = Callbacks.lookup(
Call);
220 (this->*(*Handler))(
Call,
C);
224void DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf(
225 const CallEvent &
Call, CheckerContext &
C)
const {
228 if (
Call.getNumArgs() != 1) {
229 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
230 auto BR = std::make_unique<PathSensitiveBugReport>(
232 "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
234 C.emitReport(std::move(BR));
239 SVal ArgSVal =
Call.getArgSVal(0);
240 const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
245 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
246 llvm::SmallVector<std::string> RegionNames =
247 to_vector(map_range(llvm::make_pointee_range(*SourceSet),
249 llvm::sort(RegionNames);
251 llvm::SmallString<128> Str;
252 llvm::raw_svector_ostream
OS(Str);
253 OS <<
" Origin '" << ArgSVal <<
"' bound to ";
254 llvm::interleaveComma(RegionNames,
OS,
255 [&](StringRef Name) {
OS <<
"'" << Name <<
"'"; });
256 C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg,
OS.str(), N));
260void ento::registerLifetimeModeling(CheckerManager &Mgr) {
264bool ento::shouldRegisterLifetimeModeling(
const CheckerManager &Mgr) {
268void ento::registerDebugLifetimeModeling(CheckerManager &Mgr) {
272bool 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) 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 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.
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)
The JSON file list parser is used to communicate input to InstallAPI.