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> {
57 std::unique_ptr<BugType> DoubleCloseBugType;
58 std::unique_ptr<BugType> LeakBugType;
60 void reportDoubleClose(
SymbolRef FileDescSym,
70 SimpleStreamChecker();
92SimpleStreamChecker::SimpleStreamChecker()
93 : OpenFn({
"fopen"}, 2), CloseFn({
"fclose"}, 1) {
95 DoubleCloseBugType.reset(
96 new BugType(
this,
"Double fclose",
"Unix Stream API Error"));
100 new BugType(
this,
"Resource Leak",
"Unix Stream API Error",
104void SimpleStreamChecker::checkPostCall(
const CallEvent &
Call,
106 if (!
Call.isGlobalCFunction())
109 if (!OpenFn.matches(
Call))
119 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
120 C.addTransition(State);
125 if (!
Call.isGlobalCFunction())
128 if (!CloseFn.matches(
Call))
138 const StreamState *SS = State->get<StreamMap>(FileDesc);
139 if (SS && SS->isClosed()) {
140 reportDoubleClose(FileDesc,
Call,
C);
145 State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
146 C.addTransition(State);
151 if (IsSymDead && SS.isOpened()) {
161void SimpleStreamChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
164 SymbolVector LeakedStreams;
165 StreamMapTy TrackedStreams = State->get<StreamMap>();
166 for (
auto [Sym, StreamStatus] : TrackedStreams) {
167 bool IsSymDead = SymReaper.
isDead(Sym);
170 if (
isLeaked(Sym, StreamStatus, IsSymDead, State))
171 LeakedStreams.push_back(Sym);
175 State = State->remove<StreamMap>(Sym);
181 reportLeaks(LeakedStreams,
C, N);
184void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
194 auto R = std::make_unique<PathSensitiveBugReport>(
195 *DoubleCloseBugType,
"Closing a previously closed file stream", ErrNode);
196 R->addRange(
Call.getSourceRange());
197 R->markInteresting(FileDescSym);
198 C.emitReport(std::move(R));
206 for (
SymbolRef LeakedStream : LeakedStreams) {
207 auto R = std::make_unique<PathSensitiveBugReport>(
208 *LeakBugType,
"Opened file is never closed; potential resource leak",
210 R->markInteresting(LeakedStream);
211 C.emitReport(std::move(R));
215bool SimpleStreamChecker::guaranteedNotToCloseFile(
const CallEvent &
Call)
const{
217 if (!
Call.isInSystemHeader())
221 if (
Call.argumentsMayEscape())
245 State = State->remove<StreamMap>(Sym);
255bool 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)
This class represents a description of a function call using the number of arguments and the name of ...
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
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...
A class responsible for cleaning up unused symbols.
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.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)