25 using namespace clang;
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 {
51 class SimpleStreamChecker :
public Checker<check::PostCall,
54 check::PointerEscape> {
55 CallDescription OpenFn, CloseFn;
57 std::unique_ptr<BugType> DoubleCloseBugType;
58 std::unique_ptr<BugType> LeakBugType;
60 void reportDoubleClose(
SymbolRef FileDescSym,
61 const CallEvent &Call,
62 CheckerContext &C)
const;
65 ExplodedNode *ErrNode)
const;
67 bool guaranteedNotToCloseFile(
const CallEvent &Call)
const;
70 SimpleStreamChecker();
73 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
75 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
77 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
82 const CallEvent *Call,
92 SimpleStreamChecker::SimpleStreamChecker()
93 : OpenFn(
"fopen"), 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",
104 void SimpleStreamChecker::checkPostCall(
const CallEvent &Call,
105 CheckerContext &
C)
const {
106 if (!
Call.isGlobalCFunction())
109 if (!OpenFn.matches(Call))
119 State =
State->set<StreamMap>(FileDesc, StreamState::getOpened());
123 void SimpleStreamChecker::checkPreCall(
const CallEvent &Call,
124 CheckerContext &
C)
const {
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());
151 if (IsSymDead && SS.isOpened()) {
154 ConstraintManager &CMgr =
State->getConstraintManager();
155 ConditionTruthVal OpenFailed = CMgr.isNull(
State, Sym);
156 return !OpenFailed.isConstrainedTrue();
161 void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
162 CheckerContext &
C)
const {
164 SymbolVector LeakedStreams;
165 StreamMapTy TrackedStreams =
State->get<StreamMap>();
166 for (StreamMapTy::iterator I = TrackedStreams.begin(),
167 E = TrackedStreams.end(); I != E; ++I) {
169 bool IsSymDead = SymReaper.isDead(Sym);
173 LeakedStreams.push_back(Sym);
180 ExplodedNode *N =
C.generateNonFatalErrorNode(
State);
183 reportLeaks(LeakedStreams,
C, N);
186 void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
187 const CallEvent &Call,
188 CheckerContext &
C)
const {
190 ExplodedNode *ErrNode =
C.generateErrorNode();
196 auto R = std::make_unique<PathSensitiveBugReport>(
197 *DoubleCloseBugType,
"Closing a previously closed file stream", ErrNode);
198 R->addRange(
Call.getSourceRange());
199 R->markInteresting(FileDescSym);
200 C.emitReport(std::move(R));
205 ExplodedNode *ErrNode)
const {
208 for (
SymbolRef LeakedStream : LeakedStreams) {
209 auto R = std::make_unique<PathSensitiveBugReport>(
210 *LeakBugType,
"Opened file is never closed; potential resource leak",
212 R->markInteresting(LeakedStream);
213 C.emitReport(std::move(R));
217 bool SimpleStreamChecker::guaranteedNotToCloseFile(
const CallEvent &Call)
const{
219 if (!
Call.isInSystemHeader())
223 if (
Call.argumentsMayEscape())
237 const CallEvent *Call,
244 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
256 void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
257 mgr.registerChecker<SimpleStreamChecker>();
261 bool ento::shouldRegisterSimpleStreamChecker(
const CheckerManager &mgr) {