9#include "llvm/Support/raw_ostream.h"
19class LifetimeModeling :
public Checker<check::PostCall, check::DeadSymbols> {
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;
36 if (
const auto *StackSpace =
38 const StackFrame *SF = StackSpace->getStackFrame();
40 if (SF == CurrentSF || !SF->
isParentOf(CurrentSF))
48 std::vector<const MemRegion *> Regions;
49 if (
auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
50 for (
const MemRegion *Region : *SourceSet) {
52 Regions.push_back(Region);
60 LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
61 const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
63 LifetimeSourceSet
Set = LSet ? *LSet : F.getEmptySet();
65 State = State->set<LifetimeBoundMap>(RetVal,
Set);
73 const auto *FC = dyn_cast<AnyFunctionCall>(&
Call);
77 const FunctionDecl *FD = FC->getDecl();
81 SVal RetVal =
Call.getReturnValue();
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);
92 const auto *IC = dyn_cast<CXXInstanceCall>(&
Call);
94 if (
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())
97 C.addTransition(State);
100void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
101 CheckerContext &
C)
const {
103 LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
105 for (SVal Val : llvm::make_first_range(LBMap)) {
106 if (
const auto *R = Val.getAsRegion(); R && SymReaper.
isLiveRegion(R))
113 State = State->remove<LifetimeBoundMap>(Val);
115 C.addTransition(State);
118void LifetimeModeling::printState(raw_ostream &Out,
ProgramStateRef State,
119 const char *NL,
const char *Sep)
const {
120 auto LBMap = State->get<LifetimeBoundMap>();
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;
135class DebugLifetimeModeling :
public Checker<eval::Call> {
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;
144 const CallDescriptionMap<FnCheck> Callbacks = {
145 {{CDM::SimpleFunc, {
"clang_analyzer_dumpLifetimeOriginsOf"}},
146 &DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf},
152bool DebugLifetimeModeling::evalCall(
const CallEvent &
Call,
153 CheckerContext &
C)
const {
154 if (!isa_and_nonnull<CallExpr>(
Call.getOriginExpr()))
157 const FnCheck *Handler = Callbacks.lookup(
Call);
161 (this->*(*Handler))(
Call,
C);
165void DebugLifetimeModeling::analyzerDumpLifetimeOriginsOf(
166 const CallEvent &
Call, CheckerContext &
C)
const {
169 if (
Call.getNumArgs() != 1) {
170 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
171 auto BR = std::make_unique<PathSensitiveBugReport>(
173 "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
175 C.emitReport(std::move(BR));
180 SVal ArgSVal =
Call.getArgSVal(0);
181 const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
186 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
187 llvm::SmallVector<std::string> RegionNames =
188 to_vector(map_range(llvm::make_pointee_range(*SourceSet),
190 llvm::sort(RegionNames);
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));
200void ento::registerLifetimeModeling(CheckerManager &Mgr) {
204bool ento::shouldRegisterLifetimeModeling(
const CheckerManager &Mgr) {
208void ento::registerDebugLifetimeModeling(CheckerManager &Mgr) {
212bool 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,...
ArrayRef< ParmVarDecl * > parameters() const
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 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.
const MemRegion * getAsRegion() const
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
@ 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.