33 enum Kind { Opened, Closed } K;
34 StreamState(Kind InK) : K(InK) { }
37 bool isOpened()
const {
return K == Opened; }
38 bool isClosed()
const {
return K == Closed; }
40 static StreamState getOpened() {
return StreamState(Opened); }
41 static StreamState getClosed() {
return StreamState(Closed); }
46 void Profile(llvm::FoldingSetNodeID &ID)
const {
51class SimpleStreamChecker :
public Checker<check::PostCall,
54 check::PointerEscape> {
55 const CallDescription OpenFn{CDM::CLibrary, {
"fopen"}, 2};
56 const CallDescription CloseFn{CDM::CLibrary, {
"fclose"}, 1};
58 const BugType DoubleCloseBugType{
this,
"Double fclose",
59 "Unix Stream API Error"};
60 const BugType LeakBugType{
this,
"Resource Leak",
"Unix Stream API Error",
63 void reportDoubleClose(
SymbolRef FileDescSym,
64 const CallEvent &
Call,
65 CheckerContext &
C)
const;
67 void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &
C,
68 ExplodedNode *ErrNode)
const;
72 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
74 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
76 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
81 const CallEvent *
Call,
103 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
104 C.addTransition(State);
107void SimpleStreamChecker::checkPreCall(
const CallEvent &
Call,
108 CheckerContext &
C)
const {
119 const StreamState *SS = State->get<StreamMap>(FileDesc);
120 if (SS && SS->isClosed()) {
121 reportDoubleClose(FileDesc,
Call,
C);
126 State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
127 C.addTransition(State);
132 if (IsSymDead && SS.isOpened()) {
142void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
143 CheckerContext &
C)
const {
145 SymbolVector LeakedStreams;
146 StreamMapTy TrackedStreams = State->get<StreamMap>();
147 for (
auto [Sym, StreamStatus] : TrackedStreams) {
148 bool IsSymDead = SymReaper.
isDead(Sym);
151 if (
isLeaked(Sym, StreamStatus, IsSymDead, State))
152 LeakedStreams.push_back(Sym);
156 State = State->remove<StreamMap>(Sym);
159 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
162 reportLeaks(LeakedStreams,
C, N);
165void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
166 const CallEvent &
Call,
167 CheckerContext &
C)
const {
169 ExplodedNode *ErrNode =
C.generateErrorNode();
175 auto R = std::make_unique<PathSensitiveBugReport>(
176 DoubleCloseBugType,
"Closing a previously closed file stream", ErrNode);
177 R->addRange(
Call.getSourceRange());
178 R->markInteresting(FileDescSym);
179 C.emitReport(std::move(R));
182void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
184 ExplodedNode *ErrNode)
const {
187 for (
SymbolRef LeakedStream : LeakedStreams) {
188 auto R = std::make_unique<PathSensitiveBugReport>(
189 LeakBugType,
"Opened file is never closed; potential resource leak",
191 R->markInteresting(LeakedStream);
192 C.emitReport(std::move(R));
201 const CallEvent *
Call,
211 State = State->remove<StreamMap>(Sym);
216void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
221bool ento::shouldRegisterSimpleStreamChecker(
const CheckerManager &mgr) {
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static bool isLeaked(SymbolRef Sym, const StreamState &SS, bool IsSymDead, ProgramStateRef State)
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
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.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym)
Convenience method to query the state to see if a symbol is null or not null, or if neither assumptio...
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Top level wrappers for InstallAPI frontend operations.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)