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