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> {
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,
95 if (!OpenFn.matches(
Call))
105 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
106 C.addTransition(State);
111 if (!CloseFn.matches(
Call))
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,
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);
164 reportLeaks(LeakedStreams,
C, N);
167void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
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));
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())
228 State = State->remove<StreamMap>(Sym);
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)
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
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.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)