8#include "llvm/Support/raw_ostream.h"
17class UseAfterLifetimeEnd
18 :
public Checker<check::PostCall, check::EndFunction, check::DeadSymbols> {
20 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
21 void printState(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
22 const char *Sep)
const override;
23 void reportDanglingSource(
const MemRegion *Region, ExplodedNode *N,
24 CheckerContext &
C)
const;
26 CheckerContext &
C)
const;
27 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &
C)
const;
28 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
29 const BugType BugMsg{
this,
"UseAfterLifetimeEnd",
"LifetimeBound"};
36 LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
38 const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
39 LifetimeSourceSet
Set = LSet ? *LSet : F.getEmptySet();
41 State = State->set<LifetimeBoundMap>(RetVal,
Set);
49 const auto *FC = dyn_cast<AnyFunctionCall>(&
Call);
53 const FunctionDecl *FD = FC->getDecl();
57 SVal RetVal =
Call.getReturnValue();
59 for (
const ParmVarDecl *PVD : FD->
parameters()) {
60 if (PVD->hasAttr<LifetimeBoundAttr>()) {
61 unsigned Idx = PVD->getFunctionScopeIndex();
62 SVal Arg =
Call.getArgSVal(Idx);
63 if (
const MemRegion *ArgValRegion = Arg.
getAsRegion())
64 State =
bindValues(State, RetVal, ArgValRegion);
68 if (
const auto *IC = dyn_cast<CXXInstanceCall>(&
Call)) {
70 if (
const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
75 C.addTransition(State);
85 if (
const auto *StackSpace =
87 const StackFrame *SF = StackSpace->getStackFrame();
89 if (SF == CurrentSF || !SF->
isParentOf(CurrentSF))
95void UseAfterLifetimeEnd::checkReturnedBorrower(SVal Val,
ProgramStateRef State,
96 CheckerContext &
C)
const {
97 if (
auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
98 ExplodedNode *N =
nullptr;
99 for (
const MemRegion *Region : *SourceSet) {
102 N =
C.generateNonFatalErrorNode();
105 reportDanglingSource(Region, N,
C);
111void UseAfterLifetimeEnd::checkEndFunction(
const ReturnStmt *RS,
112 CheckerContext &
C)
const {
117 auto LBMap = State->get<LifetimeBoundMap>();
127 SVal RetVal =
C.getSVal(RetExpr);
128 checkReturnedBorrower(RetVal, State,
C);
131void UseAfterLifetimeEnd::reportDanglingSource(
const MemRegion *Region,
133 CheckerContext &
C)
const {
134 auto BR = std::make_unique<PathSensitiveBugReport>(
136 (llvm::Twine(
"Returning value bound to '") + Region->
getString() +
137 "' that will go out of scope"),
139 C.emitReport(std::move(BR));
142void UseAfterLifetimeEnd::checkDeadSymbols(SymbolReaper &SymReaper,
143 CheckerContext &
C)
const {
145 LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
147 for (SVal Val : llvm::make_first_range(LBMap)) {
148 if (
const MemRegion *ValRegion = Val.
getAsRegion()) {
150 State = State->remove<LifetimeBoundMap>(Val);
153 if (!SymReaper.
isLive(ValRef))
154 State = State->remove<LifetimeBoundMap>(Val);
158 C.addTransition(State);
161void UseAfterLifetimeEnd::printState(raw_ostream &Out,
ProgramStateRef State,
162 const char *NL,
const char *Sep)
const {
163 auto LBMap = State->get<LifetimeBoundMap>();
168 Out << Sep <<
"LifetimeBound bindings:" << NL;
169 for (
auto &&[OriginSym, SourceSet] : LBMap) {
170 for (
const auto *Region : SourceSet)
171 Out <<
" Origin " << OriginSym <<
" contains Loan " << Region << NL;
176class DebugUseAfterLifetimeEnd :
public Checker<eval::Call> {
178 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
179 void analyzerDumpLifetimeOriginsOf(
const CallEvent &
Call,
180 CheckerContext &
C)
const;
182 const BugType BugMsg{
this,
"DebugUseAfterLifetimeEnd",
183 "DebugUseAfterLifetimeEnd"};
184 using FnCheck = void (DebugUseAfterLifetimeEnd::*)(
const CallEvent &
Call,
185 CheckerContext &
C)
const;
187 const CallDescriptionMap<FnCheck> Callbacks = {
188 {{CDM::SimpleFunc, {
"clang_analyzer_dumpLifetimeOriginsOf"}},
189 &DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf},
195bool DebugUseAfterLifetimeEnd::evalCall(
const CallEvent &
Call,
196 CheckerContext &
C)
const {
197 const auto *CE = dyn_cast_if_present<CallExpr>(
Call.getOriginExpr());
201 const FnCheck *Handler = Callbacks.lookup(
Call);
205 (this->*(*Handler))(
Call,
C);
209void DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf(
210 const CallEvent &
Call, CheckerContext &
C)
const {
213 if (
Call.getNumArgs() != 1) {
214 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
215 auto BR = std::make_unique<PathSensitiveBugReport>(
217 "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
219 C.emitReport(std::move(BR));
224 SVal ArgSVal =
Call.getArgSVal(0);
225 const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
230 if (ExplodedNode *N =
C.generateNonFatalErrorNode()) {
231 llvm::SmallVector<std::string> RegionNames =
232 to_vector(map_range(llvm::make_pointee_range(*SourceSet),
234 llvm::sort(RegionNames);
236 llvm::SmallString<128> Str;
237 llvm::raw_svector_ostream
OS(Str);
238 OS <<
" Origin " << ArgSVal <<
" bound to ";
239 llvm::interleaveComma(RegionNames,
OS);
240 C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg,
OS.str(), N));
244void ento::registerUseAfterLifetimeEnd(CheckerManager &Mgr) {
248bool ento::shouldRegisterUseAfterLifetimeEnd(
const CheckerManager &Mgr) {
252void ento::registerDebugUseAfterLifetimeEnd(CheckerManager &Mgr) {
256bool ento::shouldRegisterDebugUseAfterLifetimeEnd(
const CheckerManager &Mgr) {
#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,...
static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State, CheckerContext &C)
static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source)
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
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.
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)
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.