SimpleStreamChecker.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // Defines a checker for proper use of fopen/fclose APIs.
  11. // - If a file has been closed with fclose, it should not be accessed again.
  12. // Accessing a closed file results in undefined behavior.
  13. // - If a file was opened with fopen, it must be closed with fclose before
  14. // the execution ends. Failing to do so results in a resource leak.
  15. //
  16. //===----------------------------------------------------------------------===//
  17. #include "ClangSACheckers.h"
  18. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  19. #include "clang/StaticAnalyzer/Core/Checker.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. typedef SmallVector<SymbolRef, 2> SymbolVector;
  26. struct StreamState {
  27. private:
  28. enum Kind { Opened, Closed } K;
  29. StreamState(Kind InK) : K(InK) { }
  30. public:
  31. bool isOpened() const { return K == Opened; }
  32. bool isClosed() const { return K == Closed; }
  33. static StreamState getOpened() { return StreamState(Opened); }
  34. static StreamState getClosed() { return StreamState(Closed); }
  35. bool operator==(const StreamState &X) const {
  36. return K == X.K;
  37. }
  38. void Profile(llvm::FoldingSetNodeID &ID) const {
  39. ID.AddInteger(K);
  40. }
  41. };
  42. class SimpleStreamChecker : public Checker<check::PostCall,
  43. check::PreCall,
  44. check::DeadSymbols,
  45. check::PointerEscape> {
  46. mutable IdentifierInfo *IIfopen, *IIfclose;
  47. std::unique_ptr<BugType> DoubleCloseBugType;
  48. std::unique_ptr<BugType> LeakBugType;
  49. void initIdentifierInfo(ASTContext &Ctx) const;
  50. void reportDoubleClose(SymbolRef FileDescSym,
  51. const CallEvent &Call,
  52. CheckerContext &C) const;
  53. void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C,
  54. ExplodedNode *ErrNode) const;
  55. bool guaranteedNotToCloseFile(const CallEvent &Call) const;
  56. public:
  57. SimpleStreamChecker();
  58. /// Process fopen.
  59. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  60. /// Process fclose.
  61. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  62. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
  63. /// Stop tracking addresses which escape.
  64. ProgramStateRef checkPointerEscape(ProgramStateRef State,
  65. const InvalidatedSymbols &Escaped,
  66. const CallEvent *Call,
  67. PointerEscapeKind Kind) const;
  68. };
  69. } // end anonymous namespace
  70. /// The state of the checker is a map from tracked stream symbols to their
  71. /// state. Let's store it in the ProgramState.
  72. REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
  73. namespace {
  74. class StopTrackingCallback : public SymbolVisitor {
  75. ProgramStateRef state;
  76. public:
  77. StopTrackingCallback(ProgramStateRef st) : state(st) {}
  78. ProgramStateRef getState() const { return state; }
  79. bool VisitSymbol(SymbolRef sym) override {
  80. state = state->remove<StreamMap>(sym);
  81. return true;
  82. }
  83. };
  84. } // end anonymous namespace
  85. SimpleStreamChecker::SimpleStreamChecker()
  86. : IIfopen(nullptr), IIfclose(nullptr) {
  87. // Initialize the bug types.
  88. DoubleCloseBugType.reset(
  89. new BugType(this, "Double fclose", "Unix Stream API Error"));
  90. LeakBugType.reset(
  91. new BugType(this, "Resource Leak", "Unix Stream API Error"));
  92. // Sinks are higher importance bugs as well as calls to assert() or exit(0).
  93. LeakBugType->setSuppressOnSink(true);
  94. }
  95. void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
  96. CheckerContext &C) const {
  97. initIdentifierInfo(C.getASTContext());
  98. if (!Call.isGlobalCFunction())
  99. return;
  100. if (Call.getCalleeIdentifier() != IIfopen)
  101. return;
  102. // Get the symbolic value corresponding to the file handle.
  103. SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
  104. if (!FileDesc)
  105. return;
  106. // Generate the next transition (an edge in the exploded graph).
  107. ProgramStateRef State = C.getState();
  108. State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
  109. C.addTransition(State);
  110. }
  111. void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
  112. CheckerContext &C) const {
  113. initIdentifierInfo(C.getASTContext());
  114. if (!Call.isGlobalCFunction())
  115. return;
  116. if (Call.getCalleeIdentifier() != IIfclose)
  117. return;
  118. if (Call.getNumArgs() != 1)
  119. return;
  120. // Get the symbolic value corresponding to the file handle.
  121. SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
  122. if (!FileDesc)
  123. return;
  124. // Check if the stream has already been closed.
  125. ProgramStateRef State = C.getState();
  126. const StreamState *SS = State->get<StreamMap>(FileDesc);
  127. if (SS && SS->isClosed()) {
  128. reportDoubleClose(FileDesc, Call, C);
  129. return;
  130. }
  131. // Generate the next transition, in which the stream is closed.
  132. State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
  133. C.addTransition(State);
  134. }
  135. static bool isLeaked(SymbolRef Sym, const StreamState &SS,
  136. bool IsSymDead, ProgramStateRef State) {
  137. if (IsSymDead && SS.isOpened()) {
  138. // If a symbol is NULL, assume that fopen failed on this path.
  139. // A symbol should only be considered leaked if it is non-null.
  140. ConstraintManager &CMgr = State->getConstraintManager();
  141. ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
  142. return !OpenFailed.isConstrainedTrue();
  143. }
  144. return false;
  145. }
  146. void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
  147. CheckerContext &C) const {
  148. ProgramStateRef State = C.getState();
  149. SymbolVector LeakedStreams;
  150. StreamMapTy TrackedStreams = State->get<StreamMap>();
  151. for (StreamMapTy::iterator I = TrackedStreams.begin(),
  152. E = TrackedStreams.end(); I != E; ++I) {
  153. SymbolRef Sym = I->first;
  154. bool IsSymDead = SymReaper.isDead(Sym);
  155. // Collect leaked symbols.
  156. if (isLeaked(Sym, I->second, IsSymDead, State))
  157. LeakedStreams.push_back(Sym);
  158. // Remove the dead symbol from the streams map.
  159. if (IsSymDead)
  160. State = State->remove<StreamMap>(Sym);
  161. }
  162. ExplodedNode *N = C.addTransition(State);
  163. reportLeaks(LeakedStreams, C, N);
  164. }
  165. void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
  166. const CallEvent &Call,
  167. CheckerContext &C) const {
  168. // We reached a bug, stop exploring the path here by generating a sink.
  169. ExplodedNode *ErrNode = C.generateSink();
  170. // If we've already reached this node on another path, return.
  171. if (!ErrNode)
  172. return;
  173. // Generate the report.
  174. auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
  175. "Closing a previously closed file stream", ErrNode);
  176. R->addRange(Call.getSourceRange());
  177. R->markInteresting(FileDescSym);
  178. C.emitReport(std::move(R));
  179. }
  180. void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
  181. CheckerContext &C,
  182. ExplodedNode *ErrNode) const {
  183. // Attach bug reports to the leak node.
  184. // TODO: Identify the leaked file descriptor.
  185. for (SymbolRef LeakedStream : LeakedStreams) {
  186. auto R = llvm::make_unique<BugReport>(*LeakBugType,
  187. "Opened file is never closed; potential resource leak", ErrNode);
  188. R->markInteresting(LeakedStream);
  189. C.emitReport(std::move(R));
  190. }
  191. }
  192. bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{
  193. // If it's not in a system header, assume it might close a file.
  194. if (!Call.isInSystemHeader())
  195. return false;
  196. // Handle cases where we know a buffer's /address/ can escape.
  197. if (Call.argumentsMayEscape())
  198. return false;
  199. // Note, even though fclose closes the file, we do not list it here
  200. // since the checker is modeling the call.
  201. return true;
  202. }
  203. // If the pointer we are tracking escaped, do not track the symbol as
  204. // we cannot reason about it anymore.
  205. ProgramStateRef
  206. SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
  207. const InvalidatedSymbols &Escaped,
  208. const CallEvent *Call,
  209. PointerEscapeKind Kind) const {
  210. // If we know that the call cannot close a file, there is nothing to do.
  211. if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) {
  212. return State;
  213. }
  214. for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
  215. E = Escaped.end();
  216. I != E; ++I) {
  217. SymbolRef Sym = *I;
  218. // The symbol escaped. Optimistically, assume that the corresponding file
  219. // handle will be closed somewhere else.
  220. State = State->remove<StreamMap>(Sym);
  221. }
  222. return State;
  223. }
  224. void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const {
  225. if (IIfopen)
  226. return;
  227. IIfopen = &Ctx.Idents.get("fopen");
  228. IIfclose = &Ctx.Idents.get("fclose");
  229. }
  230. void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
  231. mgr.registerChecker<SimpleStreamChecker>();
  232. }