StreamChecker.cpp 46 KB

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