xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
10b57cec5SDimitry Andric //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines checkers that model and check stream handling functions.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22*480093f4SDimitry Andric #include <functional>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric using namespace clang;
250b57cec5SDimitry Andric using namespace ento;
26*480093f4SDimitry Andric using namespace std::placeholders;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric namespace {
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric struct StreamState {
310b57cec5SDimitry Andric   enum Kind { Opened, Closed, OpenFailed, Escaped } K;
320b57cec5SDimitry Andric 
33*480093f4SDimitry Andric   StreamState(Kind k) : K(k) {}
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric   bool isOpened() const { return K == Opened; }
360b57cec5SDimitry Andric   bool isClosed() const { return K == Closed; }
370b57cec5SDimitry Andric   //bool isOpenFailed() const { return K == OpenFailed; }
380b57cec5SDimitry Andric   //bool isEscaped() const { return K == Escaped; }
390b57cec5SDimitry Andric 
40*480093f4SDimitry Andric   bool operator==(const StreamState &X) const { return K == X.K; }
410b57cec5SDimitry Andric 
42*480093f4SDimitry Andric   static StreamState getOpened() { return StreamState(Opened); }
43*480093f4SDimitry Andric   static StreamState getClosed() { return StreamState(Closed); }
44*480093f4SDimitry Andric   static StreamState getOpenFailed() { return StreamState(OpenFailed); }
45*480093f4SDimitry Andric   static StreamState getEscaped() { return StreamState(Escaped); }
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   void Profile(llvm::FoldingSetNodeID &ID) const {
480b57cec5SDimitry Andric     ID.AddInteger(K);
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric };
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric class StreamChecker : public Checker<eval::Call,
530b57cec5SDimitry Andric                                      check::DeadSymbols > {
540b57cec5SDimitry Andric   mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
550b57cec5SDimitry Andric       BT_doubleclose, BT_ResourceLeak;
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric public:
580b57cec5SDimitry Andric   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
590b57cec5SDimitry Andric   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric private:
62*480093f4SDimitry Andric   using FnCheck = std::function<void(const StreamChecker *, const CallEvent &,
63*480093f4SDimitry Andric                                      CheckerContext &)>;
640b57cec5SDimitry Andric 
65*480093f4SDimitry Andric   CallDescriptionMap<FnCheck> Callbacks = {
66*480093f4SDimitry Andric       {{"fopen"}, &StreamChecker::evalFopen},
67*480093f4SDimitry Andric       {{"freopen", 3}, &StreamChecker::evalFreopen},
68*480093f4SDimitry Andric       {{"tmpfile"}, &StreamChecker::evalFopen},
69*480093f4SDimitry Andric       {{"fclose", 1}, &StreamChecker::evalFclose},
70*480093f4SDimitry Andric       {{"fread", 4},
71*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
72*480093f4SDimitry Andric       {{"fwrite", 4},
73*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
74*480093f4SDimitry Andric       {{"fseek", 3}, &StreamChecker::evalFseek},
75*480093f4SDimitry Andric       {{"ftell", 1},
76*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
77*480093f4SDimitry Andric       {{"rewind", 1},
78*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
79*480093f4SDimitry Andric       {{"fgetpos", 2},
80*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
81*480093f4SDimitry Andric       {{"fsetpos", 2},
82*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
83*480093f4SDimitry Andric       {{"clearerr", 1},
84*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
85*480093f4SDimitry Andric       {{"feof", 1},
86*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
87*480093f4SDimitry Andric       {{"ferror", 1},
88*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
89*480093f4SDimitry Andric       {{"fileno", 1},
90*480093f4SDimitry Andric        std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
91*480093f4SDimitry Andric   };
920b57cec5SDimitry Andric 
93*480093f4SDimitry Andric   void evalFopen(const CallEvent &Call, CheckerContext &C) const;
94*480093f4SDimitry Andric   void evalFreopen(const CallEvent &Call, CheckerContext &C) const;
95*480093f4SDimitry Andric   void evalFclose(const CallEvent &Call, CheckerContext &C) const;
96*480093f4SDimitry Andric   void evalFseek(const CallEvent &Call, CheckerContext &C) const;
97*480093f4SDimitry Andric 
98*480093f4SDimitry Andric   void checkArgNullStream(const CallEvent &Call, CheckerContext &C,
99*480093f4SDimitry Andric                           unsigned ArgI) const;
100*480093f4SDimitry Andric   bool checkNullStream(SVal SV, CheckerContext &C,
101*480093f4SDimitry Andric                        ProgramStateRef &State) const;
102*480093f4SDimitry Andric   void checkFseekWhence(SVal SV, CheckerContext &C,
103*480093f4SDimitry Andric                         ProgramStateRef &State) const;
104*480093f4SDimitry Andric   bool checkDoubleClose(const CallEvent &Call, CheckerContext &C,
105*480093f4SDimitry Andric                         ProgramStateRef &State) const;
1060b57cec5SDimitry Andric };
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric } // end anonymous namespace
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
1140b57cec5SDimitry Andric   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
1150b57cec5SDimitry Andric   if (!FD || FD->getKind() != Decl::Function)
1160b57cec5SDimitry Andric     return false;
1170b57cec5SDimitry Andric 
118*480093f4SDimitry Andric   // Recognize "global C functions" with only integral or pointer arguments
119*480093f4SDimitry Andric   // (and matching name) as stream functions.
120*480093f4SDimitry Andric   if (!Call.isGlobalCFunction())
1210b57cec5SDimitry Andric     return false;
122*480093f4SDimitry Andric   for (auto P : Call.parameters()) {
123*480093f4SDimitry Andric     QualType T = P->getType();
124*480093f4SDimitry Andric     if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
1250b57cec5SDimitry Andric       return false;
1260b57cec5SDimitry Andric   }
1270b57cec5SDimitry Andric 
128*480093f4SDimitry Andric   const FnCheck *Callback = Callbacks.lookup(Call);
129*480093f4SDimitry Andric   if (!Callback)
130*480093f4SDimitry Andric     return false;
131*480093f4SDimitry Andric 
132*480093f4SDimitry Andric   (*Callback)(this, Call, C);
133*480093f4SDimitry Andric 
134*480093f4SDimitry Andric   return C.isDifferent();
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric 
137*480093f4SDimitry Andric void StreamChecker::evalFopen(const CallEvent &Call, CheckerContext &C) const {
1380b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
1390b57cec5SDimitry Andric   SValBuilder &svalBuilder = C.getSValBuilder();
1400b57cec5SDimitry Andric   const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
141*480093f4SDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
142*480093f4SDimitry Andric   if (!CE)
143*480093f4SDimitry Andric     return;
144*480093f4SDimitry Andric 
145*480093f4SDimitry Andric   DefinedSVal RetVal =
146*480093f4SDimitry Andric       svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
1470b57cec5SDimitry Andric           .castAs<DefinedSVal>();
1480b57cec5SDimitry Andric   state = state->BindExpr(CE, C.getLocationContext(), RetVal);
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric   ConstraintManager &CM = C.getConstraintManager();
1510b57cec5SDimitry Andric   // Bifurcate the state into two: one with a valid FILE* pointer, the other
1520b57cec5SDimitry Andric   // with a NULL.
1530b57cec5SDimitry Andric   ProgramStateRef stateNotNull, stateNull;
1540b57cec5SDimitry Andric   std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
1550b57cec5SDimitry Andric 
156*480093f4SDimitry Andric   SymbolRef Sym = RetVal.getAsSymbol();
157*480093f4SDimitry Andric   assert(Sym && "RetVal must be a symbol here.");
158*480093f4SDimitry Andric   stateNotNull = stateNotNull->set<StreamMap>(Sym, StreamState::getOpened());
159*480093f4SDimitry Andric   stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed());
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric   C.addTransition(stateNotNull);
1620b57cec5SDimitry Andric   C.addTransition(stateNull);
1630b57cec5SDimitry Andric }
1640b57cec5SDimitry Andric 
165*480093f4SDimitry Andric void StreamChecker::evalFreopen(const CallEvent &Call,
166*480093f4SDimitry Andric                                 CheckerContext &C) const {
167*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
1680b57cec5SDimitry Andric 
169*480093f4SDimitry Andric   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
170*480093f4SDimitry Andric   if (!CE)
1710b57cec5SDimitry Andric     return;
172*480093f4SDimitry Andric 
173*480093f4SDimitry Andric   Optional<DefinedSVal> StreamVal = Call.getArgSVal(2).getAs<DefinedSVal>();
174*480093f4SDimitry Andric   if (!StreamVal)
175*480093f4SDimitry Andric     return;
176*480093f4SDimitry Andric   // Do not allow NULL as passed stream pointer.
177*480093f4SDimitry Andric   // This is not specified in the man page but may crash on some system.
178*480093f4SDimitry Andric   checkNullStream(*StreamVal, C, State);
179*480093f4SDimitry Andric   // Check if error was generated.
180*480093f4SDimitry Andric   if (C.isDifferent())
181*480093f4SDimitry Andric     return;
182*480093f4SDimitry Andric 
183*480093f4SDimitry Andric   SymbolRef StreamSym = StreamVal->getAsSymbol();
184*480093f4SDimitry Andric   // Do not care about special values for stream ("(FILE *)0x12345"?).
185*480093f4SDimitry Andric   if (!StreamSym)
186*480093f4SDimitry Andric     return;
187*480093f4SDimitry Andric 
188*480093f4SDimitry Andric   // Generate state for non-failed case.
189*480093f4SDimitry Andric   // Return value is the passed stream pointer.
190*480093f4SDimitry Andric   // According to the documentations, the stream is closed first
191*480093f4SDimitry Andric   // but any close error is ignored. The state changes to (or remains) opened.
192*480093f4SDimitry Andric   ProgramStateRef StateRetNotNull =
193*480093f4SDimitry Andric       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
194*480093f4SDimitry Andric   // Generate state for NULL return value.
195*480093f4SDimitry Andric   // Stream switches to OpenFailed state.
196*480093f4SDimitry Andric   ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
197*480093f4SDimitry Andric                                                  C.getSValBuilder().makeNull());
198*480093f4SDimitry Andric 
199*480093f4SDimitry Andric   StateRetNotNull =
200*480093f4SDimitry Andric       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
201*480093f4SDimitry Andric   StateRetNull =
202*480093f4SDimitry Andric       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
203*480093f4SDimitry Andric 
204*480093f4SDimitry Andric   C.addTransition(StateRetNotNull);
205*480093f4SDimitry Andric   C.addTransition(StateRetNull);
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric 
208*480093f4SDimitry Andric void StreamChecker::evalFclose(const CallEvent &Call, CheckerContext &C) const {
209*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
210*480093f4SDimitry Andric   if (checkDoubleClose(Call, C, State))
211*480093f4SDimitry Andric     C.addTransition(State);
2120b57cec5SDimitry Andric }
2130b57cec5SDimitry Andric 
214*480093f4SDimitry Andric void StreamChecker::evalFseek(const CallEvent &Call, CheckerContext &C) const {
215*480093f4SDimitry Andric   const Expr *AE2 = Call.getArgExpr(2);
216*480093f4SDimitry Andric   if (!AE2)
2170b57cec5SDimitry Andric     return;
218*480093f4SDimitry Andric 
219*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
220*480093f4SDimitry Andric 
221*480093f4SDimitry Andric   bool StateChanged = checkNullStream(Call.getArgSVal(0), C, State);
222*480093f4SDimitry Andric   // Check if error was generated.
223*480093f4SDimitry Andric   if (C.isDifferent())
224*480093f4SDimitry Andric     return;
225*480093f4SDimitry Andric 
2260b57cec5SDimitry Andric   // Check the legality of the 'whence' argument of 'fseek'.
227*480093f4SDimitry Andric   checkFseekWhence(State->getSVal(AE2, C.getLocationContext()), C, State);
2280b57cec5SDimitry Andric 
229*480093f4SDimitry Andric   if (!C.isDifferent() && StateChanged)
230*480093f4SDimitry Andric     C.addTransition(State);
231*480093f4SDimitry Andric 
232*480093f4SDimitry Andric   return;
233*480093f4SDimitry Andric }
234*480093f4SDimitry Andric 
235*480093f4SDimitry Andric void StreamChecker::checkArgNullStream(const CallEvent &Call, CheckerContext &C,
236*480093f4SDimitry Andric                                        unsigned ArgI) const {
237*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
238*480093f4SDimitry Andric   if (checkNullStream(Call.getArgSVal(ArgI), C, State))
239*480093f4SDimitry Andric     C.addTransition(State);
240*480093f4SDimitry Andric }
241*480093f4SDimitry Andric 
242*480093f4SDimitry Andric bool StreamChecker::checkNullStream(SVal SV, CheckerContext &C,
243*480093f4SDimitry Andric                                     ProgramStateRef &State) const {
244*480093f4SDimitry Andric   Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
245*480093f4SDimitry Andric   if (!DV)
246*480093f4SDimitry Andric     return false;
247*480093f4SDimitry Andric 
248*480093f4SDimitry Andric   ConstraintManager &CM = C.getConstraintManager();
249*480093f4SDimitry Andric   ProgramStateRef StateNotNull, StateNull;
250*480093f4SDimitry Andric   std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *DV);
251*480093f4SDimitry Andric 
252*480093f4SDimitry Andric   if (!StateNotNull && StateNull) {
253*480093f4SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
254*480093f4SDimitry Andric       if (!BT_nullfp)
255*480093f4SDimitry Andric         BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
256*480093f4SDimitry Andric                                        "Stream pointer might be NULL."));
257*480093f4SDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
258*480093f4SDimitry Andric           *BT_nullfp, BT_nullfp->getDescription(), N));
259*480093f4SDimitry Andric     }
260*480093f4SDimitry Andric     return false;
261*480093f4SDimitry Andric   }
262*480093f4SDimitry Andric 
263*480093f4SDimitry Andric   if (StateNotNull) {
264*480093f4SDimitry Andric     State = StateNotNull;
265*480093f4SDimitry Andric     return true;
266*480093f4SDimitry Andric   }
267*480093f4SDimitry Andric 
268*480093f4SDimitry Andric   return false;
269*480093f4SDimitry Andric }
270*480093f4SDimitry Andric 
271*480093f4SDimitry Andric void StreamChecker::checkFseekWhence(SVal SV, CheckerContext &C,
272*480093f4SDimitry Andric                                      ProgramStateRef &State) const {
273*480093f4SDimitry Andric   Optional<nonloc::ConcreteInt> CI = SV.getAs<nonloc::ConcreteInt>();
2740b57cec5SDimitry Andric   if (!CI)
2750b57cec5SDimitry Andric     return;
2760b57cec5SDimitry Andric 
277*480093f4SDimitry Andric   int64_t X = CI->getValue().getSExtValue();
278*480093f4SDimitry Andric   if (X >= 0 && X <= 2)
2790b57cec5SDimitry Andric     return;
2800b57cec5SDimitry Andric 
281*480093f4SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
2820b57cec5SDimitry Andric     if (!BT_illegalwhence)
2830b57cec5SDimitry Andric       BT_illegalwhence.reset(
2840b57cec5SDimitry Andric           new BuiltinBug(this, "Illegal whence argument",
2850b57cec5SDimitry Andric                          "The whence argument to fseek() should be "
2860b57cec5SDimitry Andric                          "SEEK_SET, SEEK_END, or SEEK_CUR."));
287a7dea167SDimitry Andric     C.emitReport(std::make_unique<PathSensitiveBugReport>(
2880b57cec5SDimitry Andric         *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
2890b57cec5SDimitry Andric   }
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric 
292*480093f4SDimitry Andric bool StreamChecker::checkDoubleClose(const CallEvent &Call, CheckerContext &C,
293*480093f4SDimitry Andric                                      ProgramStateRef &State) const {
294*480093f4SDimitry Andric   SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
2950b57cec5SDimitry Andric   if (!Sym)
296*480093f4SDimitry Andric     return false;
2970b57cec5SDimitry Andric 
298*480093f4SDimitry Andric   const StreamState *SS = State->get<StreamMap>(Sym);
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric   // If the file stream is not tracked, return.
3010b57cec5SDimitry Andric   if (!SS)
302*480093f4SDimitry Andric     return false;
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric   // Check: Double close a File Descriptor could cause undefined behaviour.
3050b57cec5SDimitry Andric   // Conforming to man-pages
3060b57cec5SDimitry Andric   if (SS->isClosed()) {
3070b57cec5SDimitry Andric     ExplodedNode *N = C.generateErrorNode();
3080b57cec5SDimitry Andric     if (N) {
3090b57cec5SDimitry Andric       if (!BT_doubleclose)
3100b57cec5SDimitry Andric         BT_doubleclose.reset(new BuiltinBug(
3110b57cec5SDimitry Andric             this, "Double fclose", "Try to close a file Descriptor already"
3120b57cec5SDimitry Andric                                    " closed. Cause undefined behaviour."));
313a7dea167SDimitry Andric       C.emitReport(std::make_unique<PathSensitiveBugReport>(
3140b57cec5SDimitry Andric           *BT_doubleclose, BT_doubleclose->getDescription(), N));
3150b57cec5SDimitry Andric     }
316*480093f4SDimitry Andric     return false;
3170b57cec5SDimitry Andric   }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   // Close the File Descriptor.
320*480093f4SDimitry Andric   State = State->set<StreamMap>(Sym, StreamState::getClosed());
321*480093f4SDimitry Andric 
322*480093f4SDimitry Andric   return true;
3230b57cec5SDimitry Andric }
3240b57cec5SDimitry Andric 
3250b57cec5SDimitry Andric void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
3260b57cec5SDimitry Andric                                      CheckerContext &C) const {
327*480093f4SDimitry Andric   ProgramStateRef State = C.getState();
3280b57cec5SDimitry Andric 
3290b57cec5SDimitry Andric   // TODO: Clean up the state.
330*480093f4SDimitry Andric   const StreamMapTy &Map = State->get<StreamMap>();
3310b57cec5SDimitry Andric   for (const auto &I: Map) {
3320b57cec5SDimitry Andric     SymbolRef Sym = I.first;
3330b57cec5SDimitry Andric     const StreamState &SS = I.second;
3340b57cec5SDimitry Andric     if (!SymReaper.isDead(Sym) || !SS.isOpened())
3350b57cec5SDimitry Andric       continue;
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric     ExplodedNode *N = C.generateErrorNode();
3380b57cec5SDimitry Andric     if (!N)
339*480093f4SDimitry Andric       continue;
3400b57cec5SDimitry Andric 
3410b57cec5SDimitry Andric     if (!BT_ResourceLeak)
3420b57cec5SDimitry Andric       BT_ResourceLeak.reset(
3430b57cec5SDimitry Andric           new BuiltinBug(this, "Resource Leak",
3440b57cec5SDimitry Andric                          "Opened File never closed. Potential Resource leak."));
345a7dea167SDimitry Andric     C.emitReport(std::make_unique<PathSensitiveBugReport>(
3460b57cec5SDimitry Andric         *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
3470b57cec5SDimitry Andric   }
3480b57cec5SDimitry Andric }
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric void ento::registerStreamChecker(CheckerManager &mgr) {
3510b57cec5SDimitry Andric   mgr.registerChecker<StreamChecker>();
3520b57cec5SDimitry Andric }
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric bool ento::shouldRegisterStreamChecker(const LangOptions &LO) {
3550b57cec5SDimitry Andric   return true;
3560b57cec5SDimitry Andric }
357