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;
70 bool guaranteedNotToCloseFile(
const CallEvent &
Call)
const;
74 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
76 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
78 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
83 const CallEvent *
Call,
105 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
106 C.addTransition(State);
109void SimpleStreamChecker::checkPreCall(
const CallEvent &
Call,
110 CheckerContext &
C)
const {
121 const StreamState *SS = State->get<StreamMap>(FileDesc);
122 if (SS && SS->isClosed()) {
123 reportDoubleClose(FileDesc,
Call,
C);
128 State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
129 C.addTransition(State);
134 if (IsSymDead && SS.isOpened()) {
144void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
145 CheckerContext &
C)
const {
147 SymbolVector LeakedStreams;
148 StreamMapTy TrackedStreams = State->get<StreamMap>();
149 for (
auto [Sym, StreamStatus] : TrackedStreams) {
150 bool IsSymDead = SymReaper.
isDead(Sym);
153 if (
isLeaked(Sym, StreamStatus, IsSymDead, State))
154 LeakedStreams.push_back(Sym);
158 State = State->remove<StreamMap>(Sym);
161 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
164 reportLeaks(LeakedStreams,
C, N);
167void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
168 const CallEvent &
Call,
169 CheckerContext &
C)
const {
171 ExplodedNode *ErrNode =
C.generateErrorNode();
177 auto R = std::make_unique<PathSensitiveBugReport>(
178 DoubleCloseBugType,
"Closing a previously closed file stream", ErrNode);
179 R->addRange(
Call.getSourceRange());
180 R->markInteresting(FileDescSym);
181 C.emitReport(std::move(R));
184void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
186 ExplodedNode *ErrNode)
const {
189 for (
SymbolRef LeakedStream : LeakedStreams) {
190 auto R = std::make_unique<PathSensitiveBugReport>(
191 LeakBugType,
"Opened file is never closed; potential resource leak",
193 R->markInteresting(LeakedStream);
194 C.emitReport(std::move(R));
198bool SimpleStreamChecker::guaranteedNotToCloseFile(
const CallEvent &
Call)
const{
200 if (!
Call.isInSystemHeader())
204 if (
Call.argumentsMayEscape())
218 const CallEvent *
Call,
228 State = State->remove<StreamMap>(Sym);
233void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
238bool 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
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)