StreamChecker.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file defines checkers that model and check stream handling functions.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  13. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  14. #include "clang/StaticAnalyzer/Core/Checker.h"
  15. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
  22. #include <functional>
  23. using namespace clang;
  24. using namespace ento;
  25. using namespace std::placeholders;
  26. //===----------------------------------------------------------------------===//
  27. // Definition of state data structures.
  28. //===----------------------------------------------------------------------===//
  29. namespace {
  30. struct FnDescription;
  31. /// State of the stream error flags.
  32. /// Sometimes it is not known to the checker what error flags are set.
  33. /// This is indicated by setting more than one flag to true.
  34. /// This is an optimization to avoid state splits.
  35. /// A stream can either be in FEOF or FERROR but not both at the same time.
  36. /// Multiple flags are set to handle the corresponding states together.
  37. struct StreamErrorState {
  38. /// The stream can be in state where none of the error flags set.
  39. bool NoError = true;
  40. /// The stream can be in state where the EOF indicator is set.
  41. bool FEof = false;
  42. /// The stream can be in state where the error indicator is set.
  43. bool FError = false;
  44. bool isNoError() const { return NoError && !FEof && !FError; }
  45. bool isFEof() const { return !NoError && FEof && !FError; }
  46. bool isFError() const { return !NoError && !FEof && FError; }
  47. bool operator==(const StreamErrorState &ES) const {
  48. return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
  49. }
  50. bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
  51. StreamErrorState operator|(const StreamErrorState &E) const {
  52. return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
  53. }
  54. StreamErrorState operator&(const StreamErrorState &E) const {
  55. return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
  56. }
  57. StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
  58. /// Returns if the StreamErrorState is a valid object.
  59. operator bool() const { return NoError || FEof || FError; }
  60. void Profile(llvm::FoldingSetNodeID &ID) const {
  61. ID.AddBoolean(NoError);
  62. ID.AddBoolean(FEof);
  63. ID.AddBoolean(FError);
  64. }
  65. };
  66. const StreamErrorState ErrorNone{true, false, false};
  67. const StreamErrorState ErrorFEof{false, true, false};
  68. const StreamErrorState ErrorFError{false, false, true};
  69. /// Full state information about a stream pointer.
  70. struct StreamState {
  71. /// The last file operation called in the stream.
  72. const FnDescription *LastOperation;
  73. /// State of a stream symbol.
  74. /// FIXME: We need maybe an "escaped" state later.
  75. enum KindTy {
  76. Opened, /// Stream is opened.
  77. Closed, /// Closed stream (an invalid stream pointer after it was closed).
  78. OpenFailed /// The last open operation has failed.
  79. } State;
  80. /// State of the error flags.
  81. /// Ignored in non-opened stream state but must be NoError.
  82. StreamErrorState const ErrorState;
  83. /// Indicate if the file has an "indeterminate file position indicator".
  84. /// This can be set at a failing read or write or seek operation.
  85. /// If it is set no more read or write is allowed.
  86. /// This value is not dependent on the stream error flags:
  87. /// The error flag may be cleared with `clearerr` but the file position
  88. /// remains still indeterminate.
  89. /// This value applies to all error states in ErrorState except FEOF.
  90. /// An EOF+indeterminate state is the same as EOF state.
  91. bool const FilePositionIndeterminate = false;
  92. StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
  93. bool IsFilePositionIndeterminate)
  94. : LastOperation(L), State(S), ErrorState(ES),
  95. FilePositionIndeterminate(IsFilePositionIndeterminate) {
  96. assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
  97. "FilePositionIndeterminate should be false in FEof case.");
  98. assert((State == Opened || ErrorState.isNoError()) &&
  99. "ErrorState should be None in non-opened stream state.");
  100. }
  101. bool isOpened() const { return State == Opened; }
  102. bool isClosed() const { return State == Closed; }
  103. bool isOpenFailed() const { return State == OpenFailed; }
  104. bool operator==(const StreamState &X) const {
  105. // In not opened state error state should always NoError, so comparison
  106. // here is no problem.
  107. return LastOperation == X.LastOperation && State == X.State &&
  108. ErrorState == X.ErrorState &&
  109. FilePositionIndeterminate == X.FilePositionIndeterminate;
  110. }
  111. static StreamState getOpened(const FnDescription *L,
  112. const StreamErrorState &ES = ErrorNone,
  113. bool IsFilePositionIndeterminate = false) {
  114. return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
  115. }
  116. static StreamState getClosed(const FnDescription *L) {
  117. return StreamState{L, Closed, {}, false};
  118. }
  119. static StreamState getOpenFailed(const FnDescription *L) {
  120. return StreamState{L, OpenFailed, {}, false};
  121. }
  122. void Profile(llvm::FoldingSetNodeID &ID) const {
  123. ID.AddPointer(LastOperation);
  124. ID.AddInteger(State);
  125. ID.AddInteger(ErrorState);
  126. ID.AddBoolean(FilePositionIndeterminate);
  127. }
  128. };
  129. } // namespace
  130. //===----------------------------------------------------------------------===//
  131. // StreamChecker class and utility functions.
  132. //===----------------------------------------------------------------------===//
  133. namespace {
  134. class StreamChecker;
  135. using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
  136. const CallEvent &, CheckerContext &)>;
  137. using ArgNoTy = unsigned int;
  138. static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
  139. struct FnDescription {
  140. FnCheck PreFn;
  141. FnCheck EvalFn;
  142. ArgNoTy StreamArgNo;
  143. };
  144. /// Get the value of the stream argument out of the passed call event.
  145. /// The call should contain a function that is described by Desc.
  146. SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
  147. assert(Desc && Desc->StreamArgNo != ArgNone &&
  148. "Try to get a non-existing stream argument.");
  149. return Call.getArgSVal(Desc->StreamArgNo);
  150. }
  151. /// Create a conjured symbol return value for a call expression.
  152. DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
  153. assert(CE && "Expecting a call expression.");
  154. const LocationContext *LCtx = C.getLocationContext();
  155. return C.getSValBuilder()
  156. .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
  157. .castAs<DefinedSVal>();
  158. }
  159. ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
  160. const CallExpr *CE) {
  161. DefinedSVal RetVal = makeRetVal(C, CE);
  162. State = State->BindExpr(CE, C.getLocationContext(), RetVal);
  163. State = State->assume(RetVal, true);
  164. assert(State && "Assumption on new value should not fail.");
  165. return State;
  166. }
  167. ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
  168. CheckerContext &C, const CallExpr *CE) {
  169. State = State->BindExpr(CE, C.getLocationContext(),
  170. C.getSValBuilder().makeIntVal(Value, false));
  171. return State;
  172. }
  173. class StreamChecker : public Checker<check::PreCall, eval::Call,
  174. check::DeadSymbols, check::PointerEscape> {
  175. BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
  176. BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
  177. BugType BT_UseAfterOpenFailed{this, "Invalid stream",
  178. "Stream handling error"};
  179. BugType BT_IndeterminatePosition{this, "Invalid stream state",
  180. "Stream handling error"};
  181. BugType BT_IllegalWhence{this, "Illegal whence argument",
  182. "Stream handling error"};
  183. BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
  184. BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
  185. /*SuppressOnSink =*/true};
  186. public:
  187. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  188. bool evalCall(const CallEvent &Call, CheckerContext &C) const;
  189. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
  190. ProgramStateRef checkPointerEscape(ProgramStateRef State,
  191. const InvalidatedSymbols &Escaped,
  192. const CallEvent *Call,
  193. PointerEscapeKind Kind) const;
  194. /// If true, evaluate special testing stream functions.
  195. bool TestMode = false;
  196. const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
  197. private:
  198. CallDescriptionMap<FnDescription> FnDescriptions = {
  199. {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
  200. {{"freopen", 3},
  201. {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
  202. {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
  203. {{"fclose", 1},
  204. {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
  205. {{"fread", 4},
  206. {&StreamChecker::preFread,
  207. std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
  208. {{"fwrite", 4},
  209. {&StreamChecker::preFwrite,
  210. std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
  211. {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
  212. {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
  213. {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
  214. {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
  215. {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
  216. {{"clearerr", 1},
  217. {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
  218. {{"feof", 1},
  219. {&StreamChecker::preDefault,
  220. std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
  221. 0}},
  222. {{"ferror", 1},
  223. {&StreamChecker::preDefault,
  224. std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
  225. 0}},
  226. {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
  227. };
  228. CallDescriptionMap<FnDescription> FnTestDescriptions = {
  229. {{"StreamTesterChecker_make_feof_stream", 1},
  230. {nullptr,
  231. std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
  232. 0}},
  233. {{"StreamTesterChecker_make_ferror_stream", 1},
  234. {nullptr,
  235. std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
  236. ErrorFError),
  237. 0}},
  238. };
  239. void evalFopen(const FnDescription *Desc, const CallEvent &Call,
  240. CheckerContext &C) const;
  241. void preFreopen(const FnDescription *Desc, const CallEvent &Call,
  242. CheckerContext &C) const;
  243. void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
  244. CheckerContext &C) const;
  245. void evalFclose(const FnDescription *Desc, const CallEvent &Call,
  246. CheckerContext &C) const;
  247. void preFread(const FnDescription *Desc, const CallEvent &Call,
  248. CheckerContext &C) const;
  249. void preFwrite(const FnDescription *Desc, const CallEvent &Call,
  250. CheckerContext &C) const;
  251. void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
  252. CheckerContext &C, bool IsFread) const;
  253. void preFseek(const FnDescription *Desc, const CallEvent &Call,
  254. CheckerContext &C) const;
  255. void evalFseek(const FnDescription *Desc, const CallEvent &Call,
  256. CheckerContext &C) const;
  257. void preDefault(const FnDescription *Desc, const CallEvent &Call,
  258. CheckerContext &C) const;
  259. void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
  260. CheckerContext &C) const;
  261. void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
  262. CheckerContext &C,
  263. const StreamErrorState &ErrorKind) const;
  264. void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
  265. CheckerContext &C,
  266. const StreamErrorState &ErrorKind) const;
  267. /// Check that the stream (in StreamVal) is not NULL.
  268. /// If it can only be NULL a fatal error is emitted and nullptr returned.
  269. /// Otherwise the return value is a new state where the stream is constrained
  270. /// to be non-null.
  271. ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
  272. CheckerContext &C,
  273. ProgramStateRef State) const;
  274. /// Check that the stream is the opened state.
  275. /// If the stream is known to be not opened an error is generated
  276. /// and nullptr returned, otherwise the original state is returned.
  277. ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
  278. ProgramStateRef State) const;
  279. /// Check that the stream has not an invalid ("indeterminate") file position,
  280. /// generate warning for it.
  281. /// (EOF is not an invalid position.)
  282. /// The returned state can be nullptr if a fatal error was generated.
  283. /// It can return non-null state if the stream has not an invalid position or
  284. /// there is execution path with non-invalid position.
  285. ProgramStateRef
  286. ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
  287. ProgramStateRef State) const;
  288. /// Check the legality of the 'whence' argument of 'fseek'.
  289. /// Generate error and return nullptr if it is found to be illegal.
  290. /// Otherwise returns the state.
  291. /// (State is not changed here because the "whence" value is already known.)
  292. ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
  293. ProgramStateRef State) const;
  294. /// Generate warning about stream in EOF state.
  295. /// There will be always a state transition into the passed State,
  296. /// by the new non-fatal error node or (if failed) a normal transition,
  297. /// to ensure uniform handling.
  298. void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
  299. ProgramStateRef State) const;
  300. /// Emit resource leak warnings for the given symbols.
  301. /// Createn a non-fatal error node for these, and returns it (if any warnings
  302. /// were generated). Return value is non-null.
  303. ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
  304. CheckerContext &C, ExplodedNode *Pred) const;
  305. /// Find the description data of the function called by a call event.
  306. /// Returns nullptr if no function is recognized.
  307. const FnDescription *lookupFn(const CallEvent &Call) const {
  308. // Recognize "global C functions" with only integral or pointer arguments
  309. // (and matching name) as stream functions.
  310. if (!Call.isGlobalCFunction())
  311. return nullptr;
  312. for (auto P : Call.parameters()) {
  313. QualType T = P->getType();
  314. if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
  315. return nullptr;
  316. }
  317. return FnDescriptions.lookup(Call);
  318. }
  319. /// Generate a message for BugReporterVisitor if the stored symbol is
  320. /// marked as interesting by the actual bug report.
  321. // FIXME: Use lambda instead.
  322. struct NoteFn {
  323. const BugType *BT_ResourceLeak;
  324. SymbolRef StreamSym;
  325. std::string Message;
  326. std::string operator()(PathSensitiveBugReport &BR) const {
  327. if (BR.isInteresting(StreamSym) && &BR.getBugType() == BT_ResourceLeak)
  328. return Message;
  329. return "";
  330. }
  331. };
  332. const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
  333. const std::string &Message) const {
  334. return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message});
  335. }
  336. const NoteTag *constructSetEofNoteTag(CheckerContext &C,
  337. SymbolRef StreamSym) const {
  338. return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
  339. if (!BR.isInteresting(StreamSym) ||
  340. &BR.getBugType() != this->getBT_StreamEof())
  341. return "";
  342. BR.markNotInteresting(StreamSym);
  343. return "Assuming stream reaches end-of-file here";
  344. });
  345. }
  346. /// Searches for the ExplodedNode where the file descriptor was acquired for
  347. /// StreamSym.
  348. static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
  349. SymbolRef StreamSym,
  350. CheckerContext &C);
  351. };
  352. } // end anonymous namespace
  353. // This map holds the state of a stream.
  354. // The stream is identified with a SymbolRef that is created when a stream
  355. // opening function is modeled by the checker.
  356. REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
  357. inline void assertStreamStateOpened(const StreamState *SS) {
  358. assert(SS->isOpened() &&
  359. "Previous create of error node for non-opened stream failed?");
  360. }
  361. const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
  362. SymbolRef StreamSym,
  363. CheckerContext &C) {
  364. ProgramStateRef State = N->getState();
  365. // When bug type is resource leak, exploded node N may not have state info
  366. // for leaked file descriptor, but predecessor should have it.
  367. if (!State->get<StreamMap>(StreamSym))
  368. N = N->getFirstPred();
  369. const ExplodedNode *Pred = N;
  370. while (N) {
  371. State = N->getState();
  372. if (!State->get<StreamMap>(StreamSym))
  373. return Pred;
  374. Pred = N;
  375. N = N->getFirstPred();
  376. }
  377. return nullptr;
  378. }
  379. //===----------------------------------------------------------------------===//
  380. // Methods of StreamChecker.
  381. //===----------------------------------------------------------------------===//
  382. void StreamChecker::checkPreCall(const CallEvent &Call,
  383. CheckerContext &C) const {
  384. const FnDescription *Desc = lookupFn(Call);
  385. if (!Desc || !Desc->PreFn)
  386. return;
  387. Desc->PreFn(this, Desc, Call, C);
  388. }
  389. bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
  390. const FnDescription *Desc = lookupFn(Call);
  391. if (!Desc && TestMode)
  392. Desc = FnTestDescriptions.lookup(Call);
  393. if (!Desc || !Desc->EvalFn)
  394. return false;
  395. Desc->EvalFn(this, Desc, Call, C);
  396. return C.isDifferent();
  397. }
  398. void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
  399. CheckerContext &C) const {
  400. ProgramStateRef State = C.getState();
  401. const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  402. if (!CE)
  403. return;
  404. DefinedSVal RetVal = makeRetVal(C, CE);
  405. SymbolRef RetSym = RetVal.getAsSymbol();
  406. assert(RetSym && "RetVal must be a symbol here.");
  407. State = State->BindExpr(CE, C.getLocationContext(), RetVal);
  408. // Bifurcate the state into two: one with a valid FILE* pointer, the other
  409. // with a NULL.
  410. ProgramStateRef StateNotNull, StateNull;
  411. std::tie(StateNotNull, StateNull) =
  412. C.getConstraintManager().assumeDual(State, RetVal);
  413. StateNotNull =
  414. StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
  415. StateNull =
  416. StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
  417. C.addTransition(StateNotNull,
  418. constructNoteTag(C, RetSym, "Stream opened here"));
  419. C.addTransition(StateNull);
  420. }
  421. void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
  422. CheckerContext &C) const {
  423. // Do not allow NULL as passed stream pointer but allow a closed stream.
  424. ProgramStateRef State = C.getState();
  425. State = ensureStreamNonNull(getStreamArg(Desc, Call),
  426. Call.getArgExpr(Desc->StreamArgNo), C, State);
  427. if (!State)
  428. return;
  429. C.addTransition(State);
  430. }
  431. void StreamChecker::evalFreopen(const FnDescription *Desc,
  432. const CallEvent &Call,
  433. CheckerContext &C) const {
  434. ProgramStateRef State = C.getState();
  435. auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  436. if (!CE)
  437. return;
  438. Optional<DefinedSVal> StreamVal =
  439. getStreamArg(Desc, Call).getAs<DefinedSVal>();
  440. if (!StreamVal)
  441. return;
  442. SymbolRef StreamSym = StreamVal->getAsSymbol();
  443. // Do not care about concrete values for stream ("(FILE *)0x12345"?).
  444. // FIXME: Can be stdin, stdout, stderr such values?
  445. if (!StreamSym)
  446. return;
  447. // Do not handle untracked stream. It is probably escaped.
  448. if (!State->get<StreamMap>(StreamSym))
  449. return;
  450. // Generate state for non-failed case.
  451. // Return value is the passed stream pointer.
  452. // According to the documentations, the stream is closed first
  453. // but any close error is ignored. The state changes to (or remains) opened.
  454. ProgramStateRef StateRetNotNull =
  455. State->BindExpr(CE, C.getLocationContext(), *StreamVal);
  456. // Generate state for NULL return value.
  457. // Stream switches to OpenFailed state.
  458. ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
  459. C.getSValBuilder().makeNull());
  460. StateRetNotNull =
  461. StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
  462. StateRetNull =
  463. StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
  464. C.addTransition(StateRetNotNull,
  465. constructNoteTag(C, StreamSym, "Stream reopened here"));
  466. C.addTransition(StateRetNull);
  467. }
  468. void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
  469. CheckerContext &C) const {
  470. ProgramStateRef State = C.getState();
  471. SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
  472. if (!Sym)
  473. return;
  474. const StreamState *SS = State->get<StreamMap>(Sym);
  475. if (!SS)
  476. return;
  477. assertStreamStateOpened(SS);
  478. // Close the File Descriptor.
  479. // Regardless if the close fails or not, stream becomes "closed"
  480. // and can not be used any more.
  481. State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
  482. C.addTransition(State);
  483. }
  484. void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
  485. CheckerContext &C) const {
  486. ProgramStateRef State = C.getState();
  487. SVal StreamVal = getStreamArg(Desc, Call);
  488. State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
  489. State);
  490. if (!State)
  491. return;
  492. State = ensureStreamOpened(StreamVal, C, State);
  493. if (!State)
  494. return;
  495. State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
  496. if (!State)
  497. return;
  498. SymbolRef Sym = StreamVal.getAsSymbol();
  499. if (Sym && State->get<StreamMap>(Sym)) {
  500. const StreamState *SS = State->get<StreamMap>(Sym);
  501. if (SS->ErrorState & ErrorFEof)
  502. reportFEofWarning(Sym, C, State);
  503. } else {
  504. C.addTransition(State);
  505. }
  506. }
  507. void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
  508. CheckerContext &C) const {
  509. ProgramStateRef State = C.getState();
  510. SVal StreamVal = getStreamArg(Desc, Call);
  511. State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
  512. State);
  513. if (!State)
  514. return;
  515. State = ensureStreamOpened(StreamVal, C, State);
  516. if (!State)
  517. return;
  518. State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
  519. if (!State)
  520. return;
  521. C.addTransition(State);
  522. }
  523. void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
  524. const CallEvent &Call, CheckerContext &C,
  525. bool IsFread) const {
  526. ProgramStateRef State = C.getState();
  527. SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
  528. if (!StreamSym)
  529. return;
  530. const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  531. if (!CE)
  532. return;
  533. Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
  534. if (!SizeVal)
  535. return;
  536. Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
  537. if (!NMembVal)
  538. return;
  539. const StreamState *OldSS = State->get<StreamMap>(StreamSym);
  540. if (!OldSS)
  541. return;
  542. assertStreamStateOpened(OldSS);
  543. // C'99 standard, §7.19.8.1.3, the return value of fread:
  544. // The fread function returns the number of elements successfully read, which
  545. // may be less than nmemb if a read error or end-of-file is encountered. If
  546. // size or nmemb is zero, fread returns zero and the contents of the array and
  547. // the state of the stream remain unchanged.
  548. if (State->isNull(*SizeVal).isConstrainedTrue() ||
  549. State->isNull(*NMembVal).isConstrainedTrue()) {
  550. // This is the "size or nmemb is zero" case.
  551. // Just return 0, do nothing more (not clear the error flags).
  552. State = bindInt(0, State, C, CE);
  553. C.addTransition(State);
  554. return;
  555. }
  556. // Generate a transition for the success state.
  557. // If we know the state to be FEOF at fread, do not add a success state.
  558. if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
  559. ProgramStateRef StateNotFailed =
  560. State->BindExpr(CE, C.getLocationContext(), *NMembVal);
  561. if (StateNotFailed) {
  562. StateNotFailed = StateNotFailed->set<StreamMap>(
  563. StreamSym, StreamState::getOpened(Desc));
  564. C.addTransition(StateNotFailed);
  565. }
  566. }
  567. // Add transition for the failed state.
  568. Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>();
  569. assert(RetVal && "Value should be NonLoc.");
  570. ProgramStateRef StateFailed =
  571. State->BindExpr(CE, C.getLocationContext(), *RetVal);
  572. if (!StateFailed)
  573. return;
  574. auto Cond = C.getSValBuilder()
  575. .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal,
  576. C.getASTContext().IntTy)
  577. .getAs<DefinedOrUnknownSVal>();
  578. if (!Cond)
  579. return;
  580. StateFailed = StateFailed->assume(*Cond, true);
  581. if (!StateFailed)
  582. return;
  583. StreamErrorState NewES;
  584. if (IsFread)
  585. NewES =
  586. (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
  587. else
  588. NewES = ErrorFError;
  589. // If a (non-EOF) error occurs, the resulting value of the file position
  590. // indicator for the stream is indeterminate.
  591. StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
  592. StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
  593. if (IsFread && OldSS->ErrorState != ErrorFEof)
  594. C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
  595. else
  596. C.addTransition(StateFailed);
  597. }
  598. void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
  599. CheckerContext &C) const {
  600. ProgramStateRef State = C.getState();
  601. SVal StreamVal = getStreamArg(Desc, Call);
  602. State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
  603. State);
  604. if (!State)
  605. return;
  606. State = ensureStreamOpened(StreamVal, C, State);
  607. if (!State)
  608. return;
  609. State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
  610. if (!State)
  611. return;
  612. C.addTransition(State);
  613. }
  614. void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
  615. CheckerContext &C) const {
  616. ProgramStateRef State = C.getState();
  617. SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
  618. if (!StreamSym)
  619. return;
  620. const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  621. if (!CE)
  622. return;
  623. // Ignore the call if the stream is not tracked.
  624. if (!State->get<StreamMap>(StreamSym))
  625. return;
  626. DefinedSVal RetVal = makeRetVal(C, CE);
  627. // Make expression result.
  628. State = State->BindExpr(CE, C.getLocationContext(), RetVal);
  629. // Bifurcate the state into failed and non-failed.
  630. // Return zero on success, nonzero on error.
  631. ProgramStateRef StateNotFailed, StateFailed;
  632. std::tie(StateFailed, StateNotFailed) =
  633. C.getConstraintManager().assumeDual(State, RetVal);
  634. // Reset the state to opened with no error.
  635. StateNotFailed =
  636. StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
  637. // We get error.
  638. // It is possible that fseek fails but sets none of the error flags.
  639. // If fseek failed, assume that the file position becomes indeterminate in any
  640. // case.
  641. StateFailed = StateFailed->set<StreamMap>(
  642. StreamSym,
  643. StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
  644. C.addTransition(StateNotFailed);
  645. C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
  646. }
  647. void StreamChecker::evalClearerr(const FnDescription *Desc,
  648. const CallEvent &Call,
  649. CheckerContext &C) const {
  650. ProgramStateRef State = C.getState();
  651. SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
  652. if (!StreamSym)
  653. return;
  654. const StreamState *SS = State->get<StreamMap>(StreamSym);
  655. if (!SS)
  656. return;
  657. assertStreamStateOpened(SS);
  658. // FilePositionIndeterminate is not cleared.
  659. State = State->set<StreamMap>(
  660. StreamSym,
  661. StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
  662. C.addTransition(State);
  663. }
  664. void StreamChecker::evalFeofFerror(const FnDescription *Desc,
  665. const CallEvent &Call, CheckerContext &C,
  666. const StreamErrorState &ErrorKind) const {
  667. ProgramStateRef State = C.getState();
  668. SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
  669. if (!StreamSym)
  670. return;
  671. const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
  672. if (!CE)
  673. return;
  674. const StreamState *SS = State->get<StreamMap>(StreamSym);
  675. if (!SS)
  676. return;
  677. assertStreamStateOpened(SS);
  678. if (SS->ErrorState & ErrorKind) {
  679. // Execution path with error of ErrorKind.
  680. // Function returns true.
  681. // From now on it is the only one error state.
  682. ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
  683. C.addTransition(TrueState->set<StreamMap>(
  684. StreamSym, StreamState::getOpened(Desc, ErrorKind,
  685. SS->FilePositionIndeterminate &&
  686. !ErrorKind.isFEof())));
  687. }
  688. if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
  689. // Execution path(s) with ErrorKind not set.
  690. // Function returns false.
  691. // New error state is everything before minus ErrorKind.
  692. ProgramStateRef FalseState = bindInt(0, State, C, CE);
  693. C.addTransition(FalseState->set<StreamMap>(
  694. StreamSym,
  695. StreamState::getOpened(
  696. Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
  697. }
  698. }
  699. void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
  700. CheckerContext &C) const {
  701. ProgramStateRef State = C.getState();
  702. SVal StreamVal = getStreamArg(Desc, Call);
  703. State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
  704. State);
  705. if (!State)
  706. return;
  707. State = ensureStreamOpened(StreamVal, C, State);
  708. if (!State)
  709. return;
  710. C.addTransition(State);
  711. }
  712. void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
  713. const CallEvent &Call, CheckerContext &C,
  714. const StreamErrorState &ErrorKind) const {
  715. ProgramStateRef State = C.getState();
  716. SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
  717. assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
  718. const StreamState *SS = State->get<StreamMap>(StreamSym);
  719. assert(SS && "Stream should be tracked by the checker.");
  720. State = State->set<StreamMap>(
  721. StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
  722. C.addTransition(State);
  723. }
  724. ProgramStateRef
  725. StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
  726. CheckerContext &C,
  727. ProgramStateRef State) const {
  728. auto Stream = StreamVal.getAs<DefinedSVal>();
  729. if (!Stream)
  730. return State;
  731. ConstraintManager &CM = C.getConstraintManager();
  732. ProgramStateRef StateNotNull, StateNull;
  733. std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
  734. if (!StateNotNull && StateNull) {
  735. if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
  736. auto R = std::make_unique<PathSensitiveBugReport>(
  737. BT_FileNull, "Stream pointer might be NULL.", N);
  738. if (StreamE)
  739. bugreporter::trackExpressionValue(N, StreamE, *R);
  740. C.emitReport(std::move(R));
  741. }
  742. return nullptr;
  743. }
  744. return StateNotNull;
  745. }
  746. ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
  747. CheckerContext &C,
  748. ProgramStateRef State) const {
  749. SymbolRef Sym = StreamVal.getAsSymbol();
  750. if (!Sym)
  751. return State;
  752. const StreamState *SS = State->get<StreamMap>(Sym);
  753. if (!SS)
  754. return State;
  755. if (SS->isClosed()) {
  756. // Using a stream pointer after 'fclose' causes undefined behavior
  757. // according to cppreference.com .
  758. ExplodedNode *N = C.generateErrorNode();
  759. if (N) {
  760. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  761. BT_UseAfterClose,
  762. "Stream might be already closed. Causes undefined behaviour.", N));
  763. return nullptr;
  764. }
  765. return State;
  766. }
  767. if (SS->isOpenFailed()) {
  768. // Using a stream that has failed to open is likely to cause problems.
  769. // This should usually not occur because stream pointer is NULL.
  770. // But freopen can cause a state when stream pointer remains non-null but
  771. // failed to open.
  772. ExplodedNode *N = C.generateErrorNode();
  773. if (N) {
  774. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  775. BT_UseAfterOpenFailed,
  776. "Stream might be invalid after "
  777. "(re-)opening it has failed. "
  778. "Can cause undefined behaviour.",
  779. N));
  780. return nullptr;
  781. }
  782. return State;
  783. }
  784. return State;
  785. }
  786. ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
  787. SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
  788. static const char *BugMessage =
  789. "File position of the stream might be 'indeterminate' "
  790. "after a failed operation. "
  791. "Can cause undefined behavior.";
  792. SymbolRef Sym = StreamVal.getAsSymbol();
  793. if (!Sym)
  794. return State;
  795. const StreamState *SS = State->get<StreamMap>(Sym);
  796. if (!SS)
  797. return State;
  798. assert(SS->isOpened() && "First ensure that stream is opened.");
  799. if (SS->FilePositionIndeterminate) {
  800. if (SS->ErrorState & ErrorFEof) {
  801. // The error is unknown but may be FEOF.
  802. // Continue analysis with the FEOF error state.
  803. // Report warning because the other possible error states.
  804. ExplodedNode *N = C.generateNonFatalErrorNode(State);
  805. if (!N)
  806. return nullptr;
  807. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  808. BT_IndeterminatePosition, BugMessage, N));
  809. return State->set<StreamMap>(
  810. Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
  811. }
  812. // Known or unknown error state without FEOF possible.
  813. // Stop analysis, report error.
  814. ExplodedNode *N = C.generateErrorNode(State);
  815. if (N)
  816. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  817. BT_IndeterminatePosition, BugMessage, N));
  818. return nullptr;
  819. }
  820. return State;
  821. }
  822. ProgramStateRef
  823. StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
  824. ProgramStateRef State) const {
  825. Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
  826. if (!CI)
  827. return State;
  828. int64_t X = CI->getValue().getSExtValue();
  829. if (X >= 0 && X <= 2)
  830. return State;
  831. if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
  832. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  833. BT_IllegalWhence,
  834. "The whence argument to fseek() should be "
  835. "SEEK_SET, SEEK_END, or SEEK_CUR.",
  836. N));
  837. return nullptr;
  838. }
  839. return State;
  840. }
  841. void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
  842. ProgramStateRef State) const {
  843. if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
  844. auto R = std::make_unique<PathSensitiveBugReport>(
  845. BT_StreamEof,
  846. "Read function called when stream is in EOF state. "
  847. "Function has no effect.",
  848. N);
  849. R->markInteresting(StreamSym);
  850. C.emitReport(std::move(R));
  851. return;
  852. }
  853. C.addTransition(State);
  854. }
  855. ExplodedNode *
  856. StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
  857. CheckerContext &C, ExplodedNode *Pred) const {
  858. ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
  859. if (!Err)
  860. return Pred;
  861. for (SymbolRef LeakSym : LeakedSyms) {
  862. // Resource leaks can result in multiple warning that describe the same kind
  863. // of programming error:
  864. // void f() {
  865. // FILE *F = fopen("a.txt");
  866. // if (rand()) // state split
  867. // return; // warning
  868. // } // warning
  869. // While this isn't necessarily true (leaking the same stream could result
  870. // from a different kinds of errors), the reduction in redundant reports
  871. // makes this a worthwhile heuristic.
  872. // FIXME: Add a checker option to turn this uniqueing feature off.
  873. const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
  874. assert(StreamOpenNode && "Could not find place of stream opening.");
  875. PathDiagnosticLocation LocUsedForUniqueing =
  876. PathDiagnosticLocation::createBegin(
  877. StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(),
  878. StreamOpenNode->getLocationContext());
  879. std::unique_ptr<PathSensitiveBugReport> R =
  880. std::make_unique<PathSensitiveBugReport>(
  881. BT_ResourceLeak,
  882. "Opened stream never closed. Potential resource leak.", Err,
  883. LocUsedForUniqueing,
  884. StreamOpenNode->getLocationContext()->getDecl());
  885. R->markInteresting(LeakSym);
  886. C.emitReport(std::move(R));
  887. }
  888. return Err;
  889. }
  890. void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
  891. CheckerContext &C) const {
  892. ProgramStateRef State = C.getState();
  893. llvm::SmallVector<SymbolRef, 2> LeakedSyms;
  894. const StreamMapTy &Map = State->get<StreamMap>();
  895. for (const auto &I : Map) {
  896. SymbolRef Sym = I.first;
  897. const StreamState &SS = I.second;
  898. if (!SymReaper.isDead(Sym))
  899. continue;
  900. if (SS.isOpened())
  901. LeakedSyms.push_back(Sym);
  902. State = State->remove<StreamMap>(Sym);
  903. }
  904. ExplodedNode *N = C.getPredecessor();
  905. if (!LeakedSyms.empty())
  906. N = reportLeaks(LeakedSyms, C, N);
  907. C.addTransition(State, N);
  908. }
  909. ProgramStateRef StreamChecker::checkPointerEscape(
  910. ProgramStateRef State, const InvalidatedSymbols &Escaped,
  911. const CallEvent *Call, PointerEscapeKind Kind) const {
  912. // Check for file-handling system call that is not handled by the checker.
  913. // FIXME: The checker should be updated to handle all system calls that take
  914. // 'FILE*' argument. These are now ignored.
  915. if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
  916. return State;
  917. for (SymbolRef Sym : Escaped) {
  918. // The symbol escaped.
  919. // From now the stream can be manipulated in unknown way to the checker,
  920. // it is not possible to handle it any more.
  921. // Optimistically, assume that the corresponding file handle will be closed
  922. // somewhere else.
  923. // Remove symbol from state so the following stream calls on this symbol are
  924. // not handled by the checker.
  925. State = State->remove<StreamMap>(Sym);
  926. }
  927. return State;
  928. }
  929. //===----------------------------------------------------------------------===//
  930. // Checker registration.
  931. //===----------------------------------------------------------------------===//
  932. void ento::registerStreamChecker(CheckerManager &Mgr) {
  933. Mgr.registerChecker<StreamChecker>();
  934. }
  935. bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
  936. return true;
  937. }
  938. void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
  939. auto *Checker = Mgr.getChecker<StreamChecker>();
  940. Checker->TestMode = true;
  941. }
  942. bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
  943. return true;
  944. }