clang  19.0.0git
StreamChecker.cpp
Go to the documentation of this file.
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 
24 #include "llvm/ADT/Sequence.h"
25 #include <functional>
26 #include <optional>
27 
28 using namespace clang;
29 using namespace ento;
30 using namespace std::placeholders;
31 
32 //===----------------------------------------------------------------------===//
33 // Definition of state data structures.
34 //===----------------------------------------------------------------------===//
35 
36 namespace {
37 
38 struct FnDescription;
39 
40 /// State of the stream error flags.
41 /// Sometimes it is not known to the checker what error flags are set.
42 /// This is indicated by setting more than one flag to true.
43 /// This is an optimization to avoid state splits.
44 /// A stream can either be in FEOF or FERROR but not both at the same time.
45 /// Multiple flags are set to handle the corresponding states together.
46 struct StreamErrorState {
47  /// The stream can be in state where none of the error flags set.
48  bool NoError = true;
49  /// The stream can be in state where the EOF indicator is set.
50  bool FEof = false;
51  /// The stream can be in state where the error indicator is set.
52  bool FError = false;
53 
54  bool isNoError() const { return NoError && !FEof && !FError; }
55  bool isFEof() const { return !NoError && FEof && !FError; }
56  bool isFError() const { return !NoError && !FEof && FError; }
57 
58  bool operator==(const StreamErrorState &ES) const {
59  return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
60  }
61 
62  bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
63 
64  StreamErrorState operator|(const StreamErrorState &E) const {
65  return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
66  }
67 
68  StreamErrorState operator&(const StreamErrorState &E) const {
69  return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
70  }
71 
72  StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
73 
74  /// Returns if the StreamErrorState is a valid object.
75  operator bool() const { return NoError || FEof || FError; }
76 
77  void Profile(llvm::FoldingSetNodeID &ID) const {
78  ID.AddBoolean(NoError);
79  ID.AddBoolean(FEof);
80  ID.AddBoolean(FError);
81  }
82 };
83 
84 const StreamErrorState ErrorNone{true, false, false};
85 const StreamErrorState ErrorFEof{false, true, false};
86 const StreamErrorState ErrorFError{false, false, true};
87 
88 /// Full state information about a stream pointer.
89 struct StreamState {
90  /// The last file operation called in the stream.
91  /// Can be nullptr.
92  const FnDescription *LastOperation;
93 
94  /// State of a stream symbol.
95  enum KindTy {
96  Opened, /// Stream is opened.
97  Closed, /// Closed stream (an invalid stream pointer after it was closed).
98  OpenFailed /// The last open operation has failed.
99  } State;
100 
101  /// State of the error flags.
102  /// Ignored in non-opened stream state but must be NoError.
103  StreamErrorState const ErrorState;
104 
105  /// Indicate if the file has an "indeterminate file position indicator".
106  /// This can be set at a failing read or write or seek operation.
107  /// If it is set no more read or write is allowed.
108  /// This value is not dependent on the stream error flags:
109  /// The error flag may be cleared with `clearerr` but the file position
110  /// remains still indeterminate.
111  /// This value applies to all error states in ErrorState except FEOF.
112  /// An EOF+indeterminate state is the same as EOF state.
113  bool const FilePositionIndeterminate = false;
114 
115  StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
116  bool IsFilePositionIndeterminate)
117  : LastOperation(L), State(S), ErrorState(ES),
118  FilePositionIndeterminate(IsFilePositionIndeterminate) {
119  assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
120  "FilePositionIndeterminate should be false in FEof case.");
121  assert((State == Opened || ErrorState.isNoError()) &&
122  "ErrorState should be None in non-opened stream state.");
123  }
124 
125  bool isOpened() const { return State == Opened; }
126  bool isClosed() const { return State == Closed; }
127  bool isOpenFailed() const { return State == OpenFailed; }
128 
129  bool operator==(const StreamState &X) const {
130  // In not opened state error state should always NoError, so comparison
131  // here is no problem.
132  return LastOperation == X.LastOperation && State == X.State &&
133  ErrorState == X.ErrorState &&
134  FilePositionIndeterminate == X.FilePositionIndeterminate;
135  }
136 
137  static StreamState getOpened(const FnDescription *L,
138  const StreamErrorState &ES = ErrorNone,
139  bool IsFilePositionIndeterminate = false) {
140  return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
141  }
142  static StreamState getClosed(const FnDescription *L) {
143  return StreamState{L, Closed, {}, false};
144  }
145  static StreamState getOpenFailed(const FnDescription *L) {
146  return StreamState{L, OpenFailed, {}, false};
147  }
148 
149  void Profile(llvm::FoldingSetNodeID &ID) const {
150  ID.AddPointer(LastOperation);
151  ID.AddInteger(State);
152  ErrorState.Profile(ID);
153  ID.AddBoolean(FilePositionIndeterminate);
154  }
155 };
156 
157 } // namespace
158 
159 // This map holds the state of a stream.
160 // The stream is identified with a SymbolRef that is created when a stream
161 // opening function is modeled by the checker.
162 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
163 
164 //===----------------------------------------------------------------------===//
165 // StreamChecker class and utility functions.
166 //===----------------------------------------------------------------------===//
167 
168 namespace {
169 
170 class StreamChecker;
171 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
172  const CallEvent &, CheckerContext &)>;
173 
174 using ArgNoTy = unsigned int;
175 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
176 
177 const char *FeofNote = "Assuming stream reaches end-of-file here";
178 const char *FerrorNote = "Assuming this stream operation fails";
179 
180 struct FnDescription {
181  FnCheck PreFn;
182  FnCheck EvalFn;
183  ArgNoTy StreamArgNo;
184 };
185 
186 /// Get the value of the stream argument out of the passed call event.
187 /// The call should contain a function that is described by Desc.
188 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
189  assert(Desc && Desc->StreamArgNo != ArgNone &&
190  "Try to get a non-existing stream argument.");
191  return Call.getArgSVal(Desc->StreamArgNo);
192 }
193 
194 /// Create a conjured symbol return value for a call expression.
195 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
196  assert(CE && "Expecting a call expression.");
197 
198  const LocationContext *LCtx = C.getLocationContext();
199  return C.getSValBuilder()
200  .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
201  .castAs<DefinedSVal>();
202 }
203 
205  const CallExpr *CE) {
206  DefinedSVal RetVal = makeRetVal(C, CE);
207  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
208  State = State->assume(RetVal, true);
209  assert(State && "Assumption on new value should not fail.");
210  return State;
211 }
212 
214  CheckerContext &C, const CallExpr *CE) {
215  State = State->BindExpr(CE, C.getLocationContext(),
216  C.getSValBuilder().makeIntVal(Value, CE->getType()));
217  return State;
218 }
219 
220 inline void assertStreamStateOpened(const StreamState *SS) {
221  assert(SS->isOpened() && "Stream is expected to be opened");
222 }
223 
224 class StreamChecker : public Checker<check::PreCall, eval::Call,
225  check::DeadSymbols, check::PointerEscape> {
226  BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
227  BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
228  BugType BT_UseAfterOpenFailed{this, "Invalid stream",
229  "Stream handling error"};
230  BugType BT_IndeterminatePosition{this, "Invalid stream state",
231  "Stream handling error"};
232  BugType BT_IllegalWhence{this, "Illegal whence argument",
233  "Stream handling error"};
234  BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
235  BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
236  /*SuppressOnSink =*/true};
237 
238 public:
239  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
240  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
241  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
242  ProgramStateRef checkPointerEscape(ProgramStateRef State,
243  const InvalidatedSymbols &Escaped,
244  const CallEvent *Call,
245  PointerEscapeKind Kind) const;
246 
247  const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
248  const BugType *getBT_IndeterminatePosition() const {
249  return &BT_IndeterminatePosition;
250  }
251 
252  const NoteTag *constructSetEofNoteTag(CheckerContext &C,
253  SymbolRef StreamSym) const {
254  return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
255  if (!BR.isInteresting(StreamSym) ||
256  &BR.getBugType() != this->getBT_StreamEof())
257  return "";
258 
259  BR.markNotInteresting(StreamSym);
260 
261  return FeofNote;
262  });
263  }
264 
265  const NoteTag *constructSetErrorNoteTag(CheckerContext &C,
266  SymbolRef StreamSym) const {
267  return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
268  if (!BR.isInteresting(StreamSym) ||
269  &BR.getBugType() != this->getBT_IndeterminatePosition())
270  return "";
271 
272  BR.markNotInteresting(StreamSym);
273 
274  return FerrorNote;
275  });
276  }
277 
278  const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C,
279  SymbolRef StreamSym) const {
280  return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
281  if (!BR.isInteresting(StreamSym))
282  return "";
283 
284  if (&BR.getBugType() == this->getBT_StreamEof()) {
285  BR.markNotInteresting(StreamSym);
286  return FeofNote;
287  }
288  if (&BR.getBugType() == this->getBT_IndeterminatePosition()) {
289  BR.markNotInteresting(StreamSym);
290  return FerrorNote;
291  }
292 
293  return "";
294  });
295  }
296 
297  /// If true, evaluate special testing stream functions.
298  bool TestMode = false;
299 
300  /// If true, generate failure branches for cases that are often not checked.
301  bool PedanticMode = false;
302 
303 private:
304  CallDescriptionMap<FnDescription> FnDescriptions = {
305  {{CDM::CLibrary, {"fopen"}, 2},
306  {nullptr, &StreamChecker::evalFopen, ArgNone}},
307  {{CDM::CLibrary, {"fdopen"}, 2},
308  {nullptr, &StreamChecker::evalFopen, ArgNone}},
309  {{CDM::CLibrary, {"freopen"}, 3},
310  {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
311  {{CDM::CLibrary, {"tmpfile"}, 0},
312  {nullptr, &StreamChecker::evalFopen, ArgNone}},
313  {{CDM::CLibrary, {"fclose"}, 1},
314  {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
315  {{CDM::CLibrary, {"fread"}, 4},
316  {&StreamChecker::preRead,
317  std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
318  {{CDM::CLibrary, {"fwrite"}, 4},
319  {&StreamChecker::preWrite,
320  std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
321  {{CDM::CLibrary, {"fgetc"}, 1},
322  {&StreamChecker::preRead,
323  std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
324  {{CDM::CLibrary, {"fgets"}, 3},
325  {&StreamChecker::preRead,
326  std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
327  {{CDM::CLibrary, {"getc"}, 1},
328  {&StreamChecker::preRead,
329  std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
330  {{CDM::CLibrary, {"fputc"}, 2},
331  {&StreamChecker::preWrite,
332  std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
333  {{CDM::CLibrary, {"fputs"}, 2},
334  {&StreamChecker::preWrite,
335  std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
336  {{CDM::CLibrary, {"putc"}, 2},
337  {&StreamChecker::preWrite,
338  std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
339  {{CDM::CLibrary, {"fprintf"}},
340  {&StreamChecker::preWrite,
341  std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
342  {{CDM::CLibrary, {"vfprintf"}, 3},
343  {&StreamChecker::preWrite,
344  std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
345  {{CDM::CLibrary, {"fscanf"}},
346  {&StreamChecker::preRead,
347  std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
348  {{CDM::CLibrary, {"vfscanf"}, 3},
349  {&StreamChecker::preRead,
350  std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
351  {{CDM::CLibrary, {"ungetc"}, 2},
352  {&StreamChecker::preWrite,
353  std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
354  {{CDM::CLibrary, {"getdelim"}, 4},
355  {&StreamChecker::preRead,
356  std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
357  {{CDM::CLibrary, {"getline"}, 3},
358  {&StreamChecker::preRead,
359  std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
360  {{CDM::CLibrary, {"fseek"}, 3},
361  {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
362  {{CDM::CLibrary, {"fseeko"}, 3},
363  {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
364  {{CDM::CLibrary, {"ftell"}, 1},
365  {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
366  {{CDM::CLibrary, {"ftello"}, 1},
367  {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
368  {{CDM::CLibrary, {"fflush"}, 1},
369  {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
370  {{CDM::CLibrary, {"rewind"}, 1},
371  {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
372  {{CDM::CLibrary, {"fgetpos"}, 2},
373  {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
374  {{CDM::CLibrary, {"fsetpos"}, 2},
375  {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
376  {{CDM::CLibrary, {"clearerr"}, 1},
377  {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
378  {{CDM::CLibrary, {"feof"}, 1},
379  {&StreamChecker::preDefault,
380  std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
381  0}},
382  {{CDM::CLibrary, {"ferror"}, 1},
383  {&StreamChecker::preDefault,
384  std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
385  0}},
386  {{CDM::CLibrary, {"fileno"}, 1},
387  {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
388  };
389 
390  CallDescriptionMap<FnDescription> FnTestDescriptions = {
391  {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},
392  {nullptr,
393  std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
394  false),
395  0}},
396  {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},
397  {nullptr,
398  std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
399  ErrorFError, false),
400  0}},
401  {{CDM::SimpleFunc,
402  {"StreamTesterChecker_make_ferror_indeterminate_stream"},
403  1},
404  {nullptr,
405  std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
406  ErrorFError, true),
407  0}},
408  };
409 
410  /// Expanded value of EOF, empty before initialization.
411  mutable std::optional<int> EofVal;
412  /// Expanded value of SEEK_SET, 0 if not found.
413  mutable int SeekSetVal = 0;
414  /// Expanded value of SEEK_CUR, 1 if not found.
415  mutable int SeekCurVal = 1;
416  /// Expanded value of SEEK_END, 2 if not found.
417  mutable int SeekEndVal = 2;
418  /// The built-in va_list type is platform-specific
419  mutable QualType VaListType;
420 
421  void evalFopen(const FnDescription *Desc, const CallEvent &Call,
422  CheckerContext &C) const;
423 
424  void preFreopen(const FnDescription *Desc, const CallEvent &Call,
425  CheckerContext &C) const;
426  void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
427  CheckerContext &C) const;
428 
429  void evalFclose(const FnDescription *Desc, const CallEvent &Call,
430  CheckerContext &C) const;
431 
432  void preRead(const FnDescription *Desc, const CallEvent &Call,
433  CheckerContext &C) const;
434 
435  void preWrite(const FnDescription *Desc, const CallEvent &Call,
436  CheckerContext &C) const;
437 
438  void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
439  CheckerContext &C, bool IsFread) const;
440 
441  void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
442  CheckerContext &C, bool SingleChar) const;
443 
444  void evalFputx(const FnDescription *Desc, const CallEvent &Call,
445  CheckerContext &C, bool IsSingleChar) const;
446 
447  void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
448  CheckerContext &C) const;
449 
450  void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
451  CheckerContext &C) const;
452 
453  void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
454  CheckerContext &C) const;
455 
456  void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
457  CheckerContext &C) const;
458 
459  void preFseek(const FnDescription *Desc, const CallEvent &Call,
460  CheckerContext &C) const;
461  void evalFseek(const FnDescription *Desc, const CallEvent &Call,
462  CheckerContext &C) const;
463 
464  void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
465  CheckerContext &C) const;
466 
467  void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
468  CheckerContext &C) const;
469 
470  void evalFtell(const FnDescription *Desc, const CallEvent &Call,
471  CheckerContext &C) const;
472 
473  void evalRewind(const FnDescription *Desc, const CallEvent &Call,
474  CheckerContext &C) const;
475 
476  void preDefault(const FnDescription *Desc, const CallEvent &Call,
477  CheckerContext &C) const;
478 
479  void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
480  CheckerContext &C) const;
481 
482  void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
483  CheckerContext &C,
484  const StreamErrorState &ErrorKind) const;
485 
486  void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
487  CheckerContext &C, const StreamErrorState &ErrorKind,
488  bool Indeterminate) const;
489 
490  void preFflush(const FnDescription *Desc, const CallEvent &Call,
491  CheckerContext &C) const;
492 
493  void evalFflush(const FnDescription *Desc, const CallEvent &Call,
494  CheckerContext &C) const;
495 
496  void evalFileno(const FnDescription *Desc, const CallEvent &Call,
497  CheckerContext &C) const;
498 
499  /// Check that the stream (in StreamVal) is not NULL.
500  /// If it can only be NULL a fatal error is emitted and nullptr returned.
501  /// Otherwise the return value is a new state where the stream is constrained
502  /// to be non-null.
503  ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
504  CheckerContext &C,
505  ProgramStateRef State) const;
506 
507  /// Check that the stream is the opened state.
508  /// If the stream is known to be not opened an error is generated
509  /// and nullptr returned, otherwise the original state is returned.
510  ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
511  ProgramStateRef State) const;
512 
513  /// Check that the stream has not an invalid ("indeterminate") file position,
514  /// generate warning for it.
515  /// (EOF is not an invalid position.)
516  /// The returned state can be nullptr if a fatal error was generated.
517  /// It can return non-null state if the stream has not an invalid position or
518  /// there is execution path with non-invalid position.
520  ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
521  ProgramStateRef State) const;
522 
523  /// Check the legality of the 'whence' argument of 'fseek'.
524  /// Generate error and return nullptr if it is found to be illegal.
525  /// Otherwise returns the state.
526  /// (State is not changed here because the "whence" value is already known.)
527  ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
528  ProgramStateRef State) const;
529 
530  /// Generate warning about stream in EOF state.
531  /// There will be always a state transition into the passed State,
532  /// by the new non-fatal error node or (if failed) a normal transition,
533  /// to ensure uniform handling.
534  void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
535  ProgramStateRef State) const;
536 
537  /// Emit resource leak warnings for the given symbols.
538  /// Createn a non-fatal error node for these, and returns it (if any warnings
539  /// were generated). Return value is non-null.
540  ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
541  CheckerContext &C, ExplodedNode *Pred) const;
542 
543  /// Find the description data of the function called by a call event.
544  /// Returns nullptr if no function is recognized.
545  const FnDescription *lookupFn(const CallEvent &Call) const {
546  // Recognize "global C functions" with only integral or pointer arguments
547  // (and matching name) as stream functions.
548  for (auto *P : Call.parameters()) {
549  QualType T = P->getType();
550  if (!T->isIntegralOrEnumerationType() && !T->isPointerType() &&
551  T.getCanonicalType() != VaListType)
552  return nullptr;
553  }
554 
555  return FnDescriptions.lookup(Call);
556  }
557 
558  /// Generate a message for BugReporterVisitor if the stored symbol is
559  /// marked as interesting by the actual bug report.
560  const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym,
561  const std::string &Message) const {
562  return C.getNoteTag([this, StreamSym,
563  Message](PathSensitiveBugReport &BR) -> std::string {
564  if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
565  return Message;
566  return "";
567  });
568  }
569 
570  void initMacroValues(CheckerContext &C) const {
571  if (EofVal)
572  return;
573 
574  if (const std::optional<int> OptInt =
575  tryExpandAsInteger("EOF", C.getPreprocessor()))
576  EofVal = *OptInt;
577  else
578  EofVal = -1;
579  if (const std::optional<int> OptInt =
580  tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
581  SeekSetVal = *OptInt;
582  if (const std::optional<int> OptInt =
583  tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
584  SeekEndVal = *OptInt;
585  if (const std::optional<int> OptInt =
586  tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
587  SeekCurVal = *OptInt;
588  }
589 
590  void initVaListType(CheckerContext &C) const {
591  VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType();
592  }
593 
594  /// Searches for the ExplodedNode where the file descriptor was acquired for
595  /// StreamSym.
596  static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
597  SymbolRef StreamSym,
598  CheckerContext &C);
599 };
600 
601 struct StreamOperationEvaluator {
602  SValBuilder &SVB;
603  const ASTContext &ACtx;
604 
605  SymbolRef StreamSym = nullptr;
606  const StreamState *SS = nullptr;
607  const CallExpr *CE = nullptr;
608  StreamErrorState NewES;
609 
610  StreamOperationEvaluator(CheckerContext &C)
611  : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) {
612  ;
613  }
614 
615  bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C,
617  StreamSym = getStreamArg(Desc, Call).getAsSymbol();
618  if (!StreamSym)
619  return false;
620  SS = State->get<StreamMap>(StreamSym);
621  if (!SS)
622  return false;
623  NewES = SS->ErrorState;
624  CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
625  if (!CE)
626  return false;
627 
628  assertStreamStateOpened(SS);
629 
630  return true;
631  }
632 
633  bool isStreamEof() const { return SS->ErrorState == ErrorFEof; }
634 
635  NonLoc getZeroVal(const CallEvent &Call) {
636  return *SVB.makeZeroVal(Call.getResultType()).getAs<NonLoc>();
637  }
638 
639  ProgramStateRef setStreamState(ProgramStateRef State,
640  const StreamState &NewSS) {
641  NewES = NewSS.ErrorState;
642  return State->set<StreamMap>(StreamSym, NewSS);
643  }
644 
645  ProgramStateRef makeAndBindRetVal(ProgramStateRef State, CheckerContext &C) {
646  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
647  return State->BindExpr(CE, C.getLocationContext(), RetVal);
648  }
649 
651  uint64_t Val) {
652  return State->BindExpr(CE, C.getLocationContext(),
653  SVB.makeIntVal(Val, CE->getCallReturnType(ACtx)));
654  }
655 
657  SVal Val) {
658  return State->BindExpr(CE, C.getLocationContext(), Val);
659  }
660 
661  ProgramStateRef bindNullReturnValue(ProgramStateRef State,
662  CheckerContext &C) {
663  return State->BindExpr(CE, C.getLocationContext(),
664  C.getSValBuilder().makeNullWithType(CE->getType()));
665  }
666 
667  ProgramStateRef assumeBinOpNN(ProgramStateRef State,
669  NonLoc RHS) {
670  auto Cond = SVB.evalBinOpNN(State, Op, LHS, RHS, SVB.getConditionType())
672  if (!Cond)
673  return nullptr;
674  return State->assume(*Cond, true);
675  }
676 
678  makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) {
679  DefinedSVal RetVal = makeRetVal(C, CE);
680  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
681  return C.getConstraintManager().assumeDual(State, RetVal);
682  }
683 
684  const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) {
685  bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
686  bool SetFerror = NewES.FError && !SS->ErrorState.FError;
687  if (SetFeof && !SetFerror)
688  return Ch->constructSetEofNoteTag(C, StreamSym);
689  if (!SetFeof && SetFerror)
690  return Ch->constructSetErrorNoteTag(C, StreamSym);
691  if (SetFeof && SetFerror)
692  return Ch->constructSetEofOrErrorNoteTag(C, StreamSym);
693  return nullptr;
694  }
695 };
696 
697 } // end anonymous namespace
698 
699 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
700  SymbolRef StreamSym,
701  CheckerContext &C) {
703  // When bug type is resource leak, exploded node N may not have state info
704  // for leaked file descriptor, but predecessor should have it.
705  if (!State->get<StreamMap>(StreamSym))
706  N = N->getFirstPred();
707 
708  const ExplodedNode *Pred = N;
709  while (N) {
710  State = N->getState();
711  if (!State->get<StreamMap>(StreamSym))
712  return Pred;
713  Pred = N;
714  N = N->getFirstPred();
715  }
716 
717  return nullptr;
718 }
719 
721  const CallEvent &Call,
722  ArrayRef<unsigned int> EscapingArgs) {
723  const auto *CE = Call.getOriginExpr();
724 
725  SmallVector<SVal> EscapingVals;
726  EscapingVals.reserve(EscapingArgs.size());
727  for (auto EscArgIdx : EscapingArgs)
728  EscapingVals.push_back(Call.getArgSVal(EscArgIdx));
729  State = State->invalidateRegions(EscapingVals, CE, C.blockCount(),
730  C.getLocationContext(),
731  /*CausesPointerEscape=*/false);
732  return State;
733 }
734 
735 //===----------------------------------------------------------------------===//
736 // Methods of StreamChecker.
737 //===----------------------------------------------------------------------===//
738 
739 void StreamChecker::checkPreCall(const CallEvent &Call,
740  CheckerContext &C) const {
741  initMacroValues(C);
742  initVaListType(C);
743 
744  const FnDescription *Desc = lookupFn(Call);
745  if (!Desc || !Desc->PreFn)
746  return;
747 
748  Desc->PreFn(this, Desc, Call, C);
749 }
750 
751 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
752  const FnDescription *Desc = lookupFn(Call);
753  if (!Desc && TestMode)
754  Desc = FnTestDescriptions.lookup(Call);
755  if (!Desc || !Desc->EvalFn)
756  return false;
757 
758  Desc->EvalFn(this, Desc, Call, C);
759 
760  return C.isDifferent();
761 }
762 
763 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
764  CheckerContext &C) const {
765  ProgramStateRef State = C.getState();
766  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
767  if (!CE)
768  return;
769 
770  DefinedSVal RetVal = makeRetVal(C, CE);
771  SymbolRef RetSym = RetVal.getAsSymbol();
772  assert(RetSym && "RetVal must be a symbol here.");
773 
774  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
775 
776  // Bifurcate the state into two: one with a valid FILE* pointer, the other
777  // with a NULL.
778  ProgramStateRef StateNotNull, StateNull;
779  std::tie(StateNotNull, StateNull) =
780  C.getConstraintManager().assumeDual(State, RetVal);
781 
782  StateNotNull =
783  StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
784  StateNull =
785  StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
786 
787  C.addTransition(StateNotNull,
788  constructLeakNoteTag(C, RetSym, "Stream opened here"));
789  C.addTransition(StateNull);
790 }
791 
792 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
793  CheckerContext &C) const {
794  // Do not allow NULL as passed stream pointer but allow a closed stream.
795  ProgramStateRef State = C.getState();
796  State = ensureStreamNonNull(getStreamArg(Desc, Call),
797  Call.getArgExpr(Desc->StreamArgNo), C, State);
798  if (!State)
799  return;
800 
801  C.addTransition(State);
802 }
803 
804 void StreamChecker::evalFreopen(const FnDescription *Desc,
805  const CallEvent &Call,
806  CheckerContext &C) const {
807  ProgramStateRef State = C.getState();
808 
809  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
810  if (!CE)
811  return;
812 
813  std::optional<DefinedSVal> StreamVal =
814  getStreamArg(Desc, Call).getAs<DefinedSVal>();
815  if (!StreamVal)
816  return;
817 
818  SymbolRef StreamSym = StreamVal->getAsSymbol();
819  // Do not care about concrete values for stream ("(FILE *)0x12345"?).
820  // FIXME: Can be stdin, stdout, stderr such values?
821  if (!StreamSym)
822  return;
823 
824  // Do not handle untracked stream. It is probably escaped.
825  if (!State->get<StreamMap>(StreamSym))
826  return;
827 
828  // Generate state for non-failed case.
829  // Return value is the passed stream pointer.
830  // According to the documentations, the stream is closed first
831  // but any close error is ignored. The state changes to (or remains) opened.
832  ProgramStateRef StateRetNotNull =
833  State->BindExpr(CE, C.getLocationContext(), *StreamVal);
834  // Generate state for NULL return value.
835  // Stream switches to OpenFailed state.
836  ProgramStateRef StateRetNull =
837  State->BindExpr(CE, C.getLocationContext(),
838  C.getSValBuilder().makeNullWithType(CE->getType()));
839 
840  StateRetNotNull =
841  StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
842  StateRetNull =
843  StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
844 
845  C.addTransition(StateRetNotNull,
846  constructLeakNoteTag(C, StreamSym, "Stream reopened here"));
847  C.addTransition(StateRetNull);
848 }
849 
850 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
851  CheckerContext &C) const {
852  ProgramStateRef State = C.getState();
853  StreamOperationEvaluator E(C);
854  if (!E.Init(Desc, Call, C, State))
855  return;
856 
857  // Close the File Descriptor.
858  // Regardless if the close fails or not, stream becomes "closed"
859  // and can not be used any more.
860  State = E.setStreamState(State, StreamState::getClosed(Desc));
861 
862  // Return 0 on success, EOF on failure.
863  C.addTransition(E.bindReturnValue(State, C, 0));
864  C.addTransition(E.bindReturnValue(State, C, *EofVal));
865 }
866 
867 void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,
868  CheckerContext &C) const {
869  ProgramStateRef State = C.getState();
870  SVal StreamVal = getStreamArg(Desc, Call);
871  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
872  State);
873  if (!State)
874  return;
875  State = ensureStreamOpened(StreamVal, C, State);
876  if (!State)
877  return;
878  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
879  if (!State)
880  return;
881 
882  SymbolRef Sym = StreamVal.getAsSymbol();
883  if (Sym && State->get<StreamMap>(Sym)) {
884  const StreamState *SS = State->get<StreamMap>(Sym);
885  if (SS->ErrorState & ErrorFEof)
886  reportFEofWarning(Sym, C, State);
887  } else {
888  C.addTransition(State);
889  }
890 }
891 
892 void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,
893  CheckerContext &C) const {
894  ProgramStateRef State = C.getState();
895  SVal StreamVal = getStreamArg(Desc, Call);
896  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
897  State);
898  if (!State)
899  return;
900  State = ensureStreamOpened(StreamVal, C, State);
901  if (!State)
902  return;
903  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
904  if (!State)
905  return;
906 
907  C.addTransition(State);
908 }
909 
910 void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
911  const CallEvent &Call, CheckerContext &C,
912  bool IsFread) const {
913  ProgramStateRef State = C.getState();
914  StreamOperationEvaluator E(C);
915  if (!E.Init(Desc, Call, C, State))
916  return;
917 
918  std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
919  if (!SizeVal)
920  return;
921  std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
922  if (!NMembVal)
923  return;
924 
925  // C'99 standard, §7.19.8.1.3, the return value of fread:
926  // The fread function returns the number of elements successfully read, which
927  // may be less than nmemb if a read error or end-of-file is encountered. If
928  // size or nmemb is zero, fread returns zero and the contents of the array and
929  // the state of the stream remain unchanged.
930  if (State->isNull(*SizeVal).isConstrainedTrue() ||
931  State->isNull(*NMembVal).isConstrainedTrue()) {
932  // This is the "size or nmemb is zero" case.
933  // Just return 0, do nothing more (not clear the error flags).
934  C.addTransition(E.bindReturnValue(State, C, 0));
935  return;
936  }
937 
938  // At read, invalidate the buffer in any case of error or success,
939  // except if EOF was already present.
940  if (IsFread && !E.isStreamEof())
941  State = escapeArgs(State, C, Call, {0});
942 
943  // Generate a transition for the success state.
944  // If we know the state to be FEOF at fread, do not add a success state.
945  if (!IsFread || !E.isStreamEof()) {
946  ProgramStateRef StateNotFailed =
947  State->BindExpr(E.CE, C.getLocationContext(), *NMembVal);
948  StateNotFailed =
949  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
950  C.addTransition(StateNotFailed);
951  }
952 
953  // Add transition for the failed state.
954  // At write, add failure case only if "pedantic mode" is on.
955  if (!IsFread && !PedanticMode)
956  return;
957 
958  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
959  ProgramStateRef StateFailed =
960  State->BindExpr(E.CE, C.getLocationContext(), RetVal);
961  StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
962  if (!StateFailed)
963  return;
964 
965  StreamErrorState NewES;
966  if (IsFread)
967  NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
968  else
969  NewES = ErrorFError;
970  // If a (non-EOF) error occurs, the resulting value of the file position
971  // indicator for the stream is indeterminate.
972  StateFailed = E.setStreamState(
973  StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
974  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
975 }
976 
977 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
978  CheckerContext &C, bool SingleChar) const {
979  // `fgetc` returns the read character on success, otherwise returns EOF.
980  // `fgets` returns the read buffer address on success, otherwise returns NULL.
981 
982  ProgramStateRef State = C.getState();
983  StreamOperationEvaluator E(C);
984  if (!E.Init(Desc, Call, C, State))
985  return;
986 
987  if (!E.isStreamEof()) {
988  // If there was already EOF, assume that read buffer is not changed.
989  // Otherwise it may change at success or failure.
990  State = escapeArgs(State, C, Call, {0});
991  if (SingleChar) {
992  // Generate a transition for the success state of `fgetc`.
993  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
994  ProgramStateRef StateNotFailed =
995  State->BindExpr(E.CE, C.getLocationContext(), RetVal);
996  // The returned 'unsigned char' of `fgetc` is converted to 'int',
997  // so we need to check if it is in range [0, 255].
998  StateNotFailed = StateNotFailed->assumeInclusiveRange(
999  RetVal,
1000  E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1001  E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1002  true);
1003  if (!StateNotFailed)
1004  return;
1005  C.addTransition(StateNotFailed);
1006  } else {
1007  // Generate a transition for the success state of `fgets`.
1008  std::optional<DefinedSVal> GetBuf =
1009  Call.getArgSVal(0).getAs<DefinedSVal>();
1010  if (!GetBuf)
1011  return;
1012  ProgramStateRef StateNotFailed =
1013  State->BindExpr(E.CE, C.getLocationContext(), *GetBuf);
1014  StateNotFailed =
1015  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1016  C.addTransition(StateNotFailed);
1017  }
1018  }
1019 
1020  // Add transition for the failed state.
1021  ProgramStateRef StateFailed;
1022  if (SingleChar)
1023  StateFailed = E.bindReturnValue(State, C, *EofVal);
1024  else
1025  StateFailed = E.bindNullReturnValue(State, C);
1026 
1027  // If a (non-EOF) error occurs, the resulting value of the file position
1028  // indicator for the stream is indeterminate.
1029  StreamErrorState NewES =
1030  E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1031  StateFailed = E.setStreamState(
1032  StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1033  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1034 }
1035 
1036 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
1037  CheckerContext &C, bool IsSingleChar) const {
1038  // `fputc` returns the written character on success, otherwise returns EOF.
1039  // `fputs` returns a nonnegative value on success, otherwise returns EOF.
1040 
1041  ProgramStateRef State = C.getState();
1042  StreamOperationEvaluator E(C);
1043  if (!E.Init(Desc, Call, C, State))
1044  return;
1045 
1046  if (IsSingleChar) {
1047  // Generate a transition for the success state of `fputc`.
1048  std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1049  if (!PutVal)
1050  return;
1051  ProgramStateRef StateNotFailed =
1052  State->BindExpr(E.CE, C.getLocationContext(), *PutVal);
1053  StateNotFailed =
1054  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1055  C.addTransition(StateNotFailed);
1056  } else {
1057  // Generate a transition for the success state of `fputs`.
1058  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1059  ProgramStateRef StateNotFailed =
1060  State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1061  StateNotFailed =
1062  E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1063  if (!StateNotFailed)
1064  return;
1065  StateNotFailed =
1066  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1067  C.addTransition(StateNotFailed);
1068  }
1069 
1070  if (!PedanticMode)
1071  return;
1072 
1073  // Add transition for the failed state. The resulting value of the file
1074  // position indicator for the stream is indeterminate.
1075  ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1076  StateFailed = E.setStreamState(
1077  StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1078  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1079 }
1080 
1081 void StreamChecker::evalFprintf(const FnDescription *Desc,
1082  const CallEvent &Call,
1083  CheckerContext &C) const {
1084  if (Call.getNumArgs() < 2)
1085  return;
1086 
1087  ProgramStateRef State = C.getState();
1088  StreamOperationEvaluator E(C);
1089  if (!E.Init(Desc, Call, C, State))
1090  return;
1091 
1092  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1093  State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1094  auto Cond =
1095  E.SVB
1096  .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1097  E.SVB.getConditionType())
1098  .getAs<DefinedOrUnknownSVal>();
1099  if (!Cond)
1100  return;
1101  ProgramStateRef StateNotFailed, StateFailed;
1102  std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1103 
1104  StateNotFailed =
1105  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1106  C.addTransition(StateNotFailed);
1107 
1108  if (!PedanticMode)
1109  return;
1110 
1111  // Add transition for the failed state. The resulting value of the file
1112  // position indicator for the stream is indeterminate.
1113  StateFailed = E.setStreamState(
1114  StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1115  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1116 }
1117 
1118 void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
1119  CheckerContext &C) const {
1120  if (Call.getNumArgs() < 2)
1121  return;
1122 
1123  ProgramStateRef State = C.getState();
1124  StreamOperationEvaluator E(C);
1125  if (!E.Init(Desc, Call, C, State))
1126  return;
1127 
1128  // Add the success state.
1129  // In this context "success" means there is not an EOF or other read error
1130  // before any item is matched in 'fscanf'. But there may be match failure,
1131  // therefore return value can be 0 or greater.
1132  // It is not specified what happens if some items (not all) are matched and
1133  // then EOF or read error happens. Now this case is handled like a "success"
1134  // case, and no error flags are set on the stream. This is probably not
1135  // accurate, and the POSIX documentation does not tell more.
1136  if (!E.isStreamEof()) {
1137  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1138  ProgramStateRef StateNotFailed =
1139  State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1140  StateNotFailed =
1141  E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1142  if (!StateNotFailed)
1143  return;
1144 
1145  if (auto const *Callee = Call.getCalleeIdentifier();
1146  !Callee || Callee->getName() != "vfscanf") {
1147  SmallVector<unsigned int> EscArgs;
1148  for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
1149  EscArgs.push_back(EscArg);
1150  StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
1151  }
1152 
1153  if (StateNotFailed)
1154  C.addTransition(StateNotFailed);
1155  }
1156 
1157  // Add transition for the failed state.
1158  // Error occurs if nothing is matched yet and reading the input fails.
1159  // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
1160  // be set but it is not further specified if all are required to be set.
1161  // Documentation does not mention, but file position will be set to
1162  // indeterminate similarly as at 'fread'.
1163  ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1164  StreamErrorState NewES =
1165  E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1166  StateFailed = E.setStreamState(
1167  StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1168  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1169 }
1170 
1171 void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,
1172  CheckerContext &C) const {
1173  ProgramStateRef State = C.getState();
1174  StreamOperationEvaluator E(C);
1175  if (!E.Init(Desc, Call, C, State))
1176  return;
1177 
1178  // Generate a transition for the success state.
1179  std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1180  if (!PutVal)
1181  return;
1182  ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal);
1183  StateNotFailed =
1184  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1185  C.addTransition(StateNotFailed);
1186 
1187  // Add transition for the failed state.
1188  // Failure of 'ungetc' does not result in feof or ferror state.
1189  // If the PutVal has value of EofVal the function should "fail", but this is
1190  // the same transition as the success state.
1191  // In this case only one state transition is added by the analyzer (the two
1192  // new states may be similar).
1193  ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1194  StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1195  C.addTransition(StateFailed);
1196 }
1197 
1198 void StreamChecker::evalGetdelim(const FnDescription *Desc,
1199  const CallEvent &Call,
1200  CheckerContext &C) const {
1201  ProgramStateRef State = C.getState();
1202  StreamOperationEvaluator E(C);
1203  if (!E.Init(Desc, Call, C, State))
1204  return;
1205 
1206  // Upon successful completion, the getline() and getdelim() functions shall
1207  // return the number of bytes written into the buffer.
1208  // If the end-of-file indicator for the stream is set, the function shall
1209  // return -1.
1210  // If an error occurs, the function shall return -1 and set 'errno'.
1211 
1212  if (!E.isStreamEof()) {
1213  // Escape buffer and size (may change by the call).
1214  // May happen even at error (partial read?).
1215  State = escapeArgs(State, C, Call, {0, 1});
1216 
1217  // Add transition for the successful state.
1218  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1219  ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);
1220  StateNotFailed =
1221  E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1222 
1223  // On success, a buffer is allocated.
1224  auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State);
1225  if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
1226  StateNotFailed = StateNotFailed->assume(
1227  NewLinePtr->castAs<DefinedOrUnknownSVal>(), true);
1228 
1229  // The buffer size `*n` must be enough to hold the whole line, and
1230  // greater than the return value, since it has to account for '\0'.
1231  SVal SizePtrSval = Call.getArgSVal(1);
1232  auto NVal = getPointeeVal(SizePtrSval, State);
1233  if (NVal && isa<NonLoc>(*NVal)) {
1234  StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1235  NVal->castAs<NonLoc>(), RetVal);
1236  StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);
1237  }
1238  if (!StateNotFailed)
1239  return;
1240  C.addTransition(StateNotFailed);
1241  }
1242 
1243  // Add transition for the failed state.
1244  // If a (non-EOF) error occurs, the resulting value of the file position
1245  // indicator for the stream is indeterminate.
1246  ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1247  StreamErrorState NewES =
1248  E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1249  StateFailed = E.setStreamState(
1250  StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1251  // On failure, the content of the buffer is undefined.
1252  if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State))
1253  StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
1254  C.getLocationContext());
1255  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1256 }
1257 
1258 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
1259  CheckerContext &C) const {
1260  ProgramStateRef State = C.getState();
1261  SVal StreamVal = getStreamArg(Desc, Call);
1262  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1263  State);
1264  if (!State)
1265  return;
1266  State = ensureStreamOpened(StreamVal, C, State);
1267  if (!State)
1268  return;
1269  State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
1270  if (!State)
1271  return;
1272 
1273  C.addTransition(State);
1274 }
1275 
1276 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
1277  CheckerContext &C) const {
1278  ProgramStateRef State = C.getState();
1279  StreamOperationEvaluator E(C);
1280  if (!E.Init(Desc, Call, C, State))
1281  return;
1282 
1283  // Add success state.
1284  ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0);
1285  // No failure: Reset the state to opened with no error.
1286  StateNotFailed =
1287  E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1288  C.addTransition(StateNotFailed);
1289 
1290  if (!PedanticMode)
1291  return;
1292 
1293  // Add failure state.
1294  // At error it is possible that fseek fails but sets none of the error flags.
1295  // If fseek failed, assume that the file position becomes indeterminate in any
1296  // case.
1297  // It is allowed to set the position beyond the end of the file. EOF error
1298  // should not occur.
1299  ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1300  StateFailed = E.setStreamState(
1301  StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1302  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1303 }
1304 
1305 void StreamChecker::evalFgetpos(const FnDescription *Desc,
1306  const CallEvent &Call,
1307  CheckerContext &C) const {
1308  ProgramStateRef State = C.getState();
1309  StreamOperationEvaluator E(C);
1310  if (!E.Init(Desc, Call, C, State))
1311  return;
1312 
1313  ProgramStateRef StateNotFailed, StateFailed;
1314  std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1315  StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1});
1316 
1317  // This function does not affect the stream state.
1318  // Still we add success and failure state with the appropriate return value.
1319  // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1320  C.addTransition(StateNotFailed);
1321  C.addTransition(StateFailed);
1322 }
1323 
1324 void StreamChecker::evalFsetpos(const FnDescription *Desc,
1325  const CallEvent &Call,
1326  CheckerContext &C) const {
1327  ProgramStateRef State = C.getState();
1328  StreamOperationEvaluator E(C);
1329  if (!E.Init(Desc, Call, C, State))
1330  return;
1331 
1332  ProgramStateRef StateNotFailed, StateFailed;
1333  std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1334 
1335  StateNotFailed = E.setStreamState(
1336  StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false));
1337  C.addTransition(StateNotFailed);
1338 
1339  if (!PedanticMode)
1340  return;
1341 
1342  // At failure ferror could be set.
1343  // The standards do not tell what happens with the file position at failure.
1344  // But we can assume that it is dangerous to make a next I/O operation after
1345  // the position was not set correctly (similar to 'fseek').
1346  StateFailed = E.setStreamState(
1347  StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1348 
1349  C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1350 }
1351 
1352 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1353  CheckerContext &C) const {
1354  ProgramStateRef State = C.getState();
1355  StreamOperationEvaluator E(C);
1356  if (!E.Init(Desc, Call, C, State))
1357  return;
1358 
1359  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1360  ProgramStateRef StateNotFailed =
1361  State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1362  StateNotFailed =
1363  E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1364  if (!StateNotFailed)
1365  return;
1366 
1367  ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1368 
1369  // This function does not affect the stream state.
1370  // Still we add success and failure state with the appropriate return value.
1371  // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1372  C.addTransition(StateNotFailed);
1373  C.addTransition(StateFailed);
1374 }
1375 
1376 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1377  CheckerContext &C) const {
1378  ProgramStateRef State = C.getState();
1379  StreamOperationEvaluator E(C);
1380  if (!E.Init(Desc, Call, C, State))
1381  return;
1382 
1383  State =
1384  E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false));
1385  C.addTransition(State);
1386 }
1387 
1388 void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
1389  CheckerContext &C) const {
1390  ProgramStateRef State = C.getState();
1391  SVal StreamVal = getStreamArg(Desc, Call);
1392  std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1393  if (!Stream)
1394  return;
1395 
1396  ProgramStateRef StateNotNull, StateNull;
1397  std::tie(StateNotNull, StateNull) =
1398  C.getConstraintManager().assumeDual(State, *Stream);
1399  if (StateNotNull && !StateNull)
1400  ensureStreamOpened(StreamVal, C, StateNotNull);
1401 }
1402 
1403 void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1404  CheckerContext &C) const {
1405  ProgramStateRef State = C.getState();
1406  SVal StreamVal = getStreamArg(Desc, Call);
1407  std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1408  if (!Stream)
1409  return;
1410 
1411  // Skip if the stream can be both NULL and non-NULL.
1412  ProgramStateRef StateNotNull, StateNull;
1413  std::tie(StateNotNull, StateNull) =
1414  C.getConstraintManager().assumeDual(State, *Stream);
1415  if (StateNotNull && StateNull)
1416  return;
1417  if (StateNotNull && !StateNull)
1418  State = StateNotNull;
1419  else
1420  State = StateNull;
1421 
1422  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1423  if (!CE)
1424  return;
1425 
1426  // `fflush` returns EOF on failure, otherwise returns 0.
1427  ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1428  ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);
1429 
1430  // Clear error states if `fflush` returns 0, but retain their EOF flags.
1431  auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
1432  const StreamState *SS) {
1433  if (SS->ErrorState & ErrorFError) {
1434  StreamErrorState NewES =
1435  (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1436  StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
1437  StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1438  }
1439  };
1440 
1441  if (StateNotNull && !StateNull) {
1442  // Skip if the input stream's state is unknown, open-failed or closed.
1443  if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
1444  const StreamState *SS = State->get<StreamMap>(StreamSym);
1445  if (SS) {
1446  assert(SS->isOpened() && "Stream is expected to be opened");
1447  ClearErrorInNotFailed(StreamSym, SS);
1448  } else
1449  return;
1450  }
1451  } else {
1452  // Clear error states for all streams.
1453  const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1454  for (const auto &I : Map) {
1455  SymbolRef Sym = I.first;
1456  const StreamState &SS = I.second;
1457  if (SS.isOpened())
1458  ClearErrorInNotFailed(Sym, &SS);
1459  }
1460  }
1461 
1462  C.addTransition(StateNotFailed);
1463  C.addTransition(StateFailed);
1464 }
1465 
1466 void StreamChecker::evalClearerr(const FnDescription *Desc,
1467  const CallEvent &Call,
1468  CheckerContext &C) const {
1469  ProgramStateRef State = C.getState();
1470  StreamOperationEvaluator E(C);
1471  if (!E.Init(Desc, Call, C, State))
1472  return;
1473 
1474  // FilePositionIndeterminate is not cleared.
1475  State = E.setStreamState(
1476  State,
1477  StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1478  C.addTransition(State);
1479 }
1480 
1481 void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1482  const CallEvent &Call, CheckerContext &C,
1483  const StreamErrorState &ErrorKind) const {
1484  ProgramStateRef State = C.getState();
1485  StreamOperationEvaluator E(C);
1486  if (!E.Init(Desc, Call, C, State))
1487  return;
1488 
1489  if (E.SS->ErrorState & ErrorKind) {
1490  // Execution path with error of ErrorKind.
1491  // Function returns true.
1492  // From now on it is the only one error state.
1493  ProgramStateRef TrueState = bindAndAssumeTrue(State, C, E.CE);
1494  C.addTransition(E.setStreamState(
1495  TrueState, StreamState::getOpened(Desc, ErrorKind,
1496  E.SS->FilePositionIndeterminate &&
1497  !ErrorKind.isFEof())));
1498  }
1499  if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1500  // Execution path(s) with ErrorKind not set.
1501  // Function returns false.
1502  // New error state is everything before minus ErrorKind.
1503  ProgramStateRef FalseState = E.bindReturnValue(State, C, 0);
1504  C.addTransition(E.setStreamState(
1505  FalseState,
1506  StreamState::getOpened(
1507  Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1508  }
1509 }
1510 
1511 void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call,
1512  CheckerContext &C) const {
1513  // Fileno should fail only if the passed pointer is invalid.
1514  // Some of the preconditions are checked already in preDefault.
1515  // Here we can assume that the operation does not fail, because if we
1516  // introduced a separate branch where fileno() returns -1, then it would cause
1517  // many unexpected and unwanted warnings in situations where fileno() is
1518  // called on valid streams.
1519  // The stream error states are not modified by 'fileno', and 'errno' is also
1520  // left unchanged (so this evalCall does not invalidate it, but we have a
1521  // custom evalCall instead of the default that would invalidate it).
1522  ProgramStateRef State = C.getState();
1523  StreamOperationEvaluator E(C);
1524  if (!E.Init(Desc, Call, C, State))
1525  return;
1526 
1527  NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1528  State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1529  State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call));
1530  if (!State)
1531  return;
1532 
1533  C.addTransition(State);
1534 }
1535 
1536 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1537  CheckerContext &C) const {
1538  ProgramStateRef State = C.getState();
1539  SVal StreamVal = getStreamArg(Desc, Call);
1540  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1541  State);
1542  if (!State)
1543  return;
1544  State = ensureStreamOpened(StreamVal, C, State);
1545  if (!State)
1546  return;
1547 
1548  C.addTransition(State);
1549 }
1550 
1551 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1552  const CallEvent &Call, CheckerContext &C,
1553  const StreamErrorState &ErrorKind,
1554  bool Indeterminate) const {
1555  ProgramStateRef State = C.getState();
1556  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1557  assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1558  const StreamState *SS = State->get<StreamMap>(StreamSym);
1559  assert(SS && "Stream should be tracked by the checker.");
1560  State = State->set<StreamMap>(
1561  StreamSym,
1562  StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1563  C.addTransition(State);
1564 }
1565 
1567 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1568  CheckerContext &C,
1569  ProgramStateRef State) const {
1570  auto Stream = StreamVal.getAs<DefinedSVal>();
1571  if (!Stream)
1572  return State;
1573 
1574  ConstraintManager &CM = C.getConstraintManager();
1575 
1576  ProgramStateRef StateNotNull, StateNull;
1577  std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1578 
1579  if (!StateNotNull && StateNull) {
1580  if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1581  auto R = std::make_unique<PathSensitiveBugReport>(
1582  BT_FileNull, "Stream pointer might be NULL.", N);
1583  if (StreamE)
1584  bugreporter::trackExpressionValue(N, StreamE, *R);
1585  C.emitReport(std::move(R));
1586  }
1587  return nullptr;
1588  }
1589 
1590  return StateNotNull;
1591 }
1592 
1593 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1594  CheckerContext &C,
1595  ProgramStateRef State) const {
1596  SymbolRef Sym = StreamVal.getAsSymbol();
1597  if (!Sym)
1598  return State;
1599 
1600  const StreamState *SS = State->get<StreamMap>(Sym);
1601  if (!SS)
1602  return State;
1603 
1604  if (SS->isClosed()) {
1605  // Using a stream pointer after 'fclose' causes undefined behavior
1606  // according to cppreference.com .
1607  ExplodedNode *N = C.generateErrorNode();
1608  if (N) {
1609  C.emitReport(std::make_unique<PathSensitiveBugReport>(
1610  BT_UseAfterClose,
1611  "Stream might be already closed. Causes undefined behaviour.", N));
1612  return nullptr;
1613  }
1614 
1615  return State;
1616  }
1617 
1618  if (SS->isOpenFailed()) {
1619  // Using a stream that has failed to open is likely to cause problems.
1620  // This should usually not occur because stream pointer is NULL.
1621  // But freopen can cause a state when stream pointer remains non-null but
1622  // failed to open.
1623  ExplodedNode *N = C.generateErrorNode();
1624  if (N) {
1625  C.emitReport(std::make_unique<PathSensitiveBugReport>(
1626  BT_UseAfterOpenFailed,
1627  "Stream might be invalid after "
1628  "(re-)opening it has failed. "
1629  "Can cause undefined behaviour.",
1630  N));
1631  return nullptr;
1632  }
1633  }
1634 
1635  return State;
1636 }
1637 
1638 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1639  SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1640  static const char *BugMessage =
1641  "File position of the stream might be 'indeterminate' "
1642  "after a failed operation. "
1643  "Can cause undefined behavior.";
1644 
1645  SymbolRef Sym = StreamVal.getAsSymbol();
1646  if (!Sym)
1647  return State;
1648 
1649  const StreamState *SS = State->get<StreamMap>(Sym);
1650  if (!SS)
1651  return State;
1652 
1653  assert(SS->isOpened() && "First ensure that stream is opened.");
1654 
1655  if (SS->FilePositionIndeterminate) {
1656  if (SS->ErrorState & ErrorFEof) {
1657  // The error is unknown but may be FEOF.
1658  // Continue analysis with the FEOF error state.
1659  // Report warning because the other possible error states.
1660  ExplodedNode *N = C.generateNonFatalErrorNode(State);
1661  if (!N)
1662  return nullptr;
1663 
1664  auto R = std::make_unique<PathSensitiveBugReport>(
1665  BT_IndeterminatePosition, BugMessage, N);
1666  R->markInteresting(Sym);
1667  C.emitReport(std::move(R));
1668  return State->set<StreamMap>(
1669  Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1670  }
1671 
1672  // Known or unknown error state without FEOF possible.
1673  // Stop analysis, report error.
1674  if (ExplodedNode *N = C.generateErrorNode(State)) {
1675  auto R = std::make_unique<PathSensitiveBugReport>(
1676  BT_IndeterminatePosition, BugMessage, N);
1677  R->markInteresting(Sym);
1678  C.emitReport(std::move(R));
1679  }
1680 
1681  return nullptr;
1682  }
1683 
1684  return State;
1685 }
1686 
1688 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1689  ProgramStateRef State) const {
1690  std::optional<nonloc::ConcreteInt> CI =
1691  WhenceVal.getAs<nonloc::ConcreteInt>();
1692  if (!CI)
1693  return State;
1694 
1695  int64_t X = CI->getValue().getSExtValue();
1696  if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1697  return State;
1698 
1699  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1700  C.emitReport(std::make_unique<PathSensitiveBugReport>(
1701  BT_IllegalWhence,
1702  "The whence argument to fseek() should be "
1703  "SEEK_SET, SEEK_END, or SEEK_CUR.",
1704  N));
1705  return nullptr;
1706  }
1707 
1708  return State;
1709 }
1710 
1711 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1712  ProgramStateRef State) const {
1713  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1714  auto R = std::make_unique<PathSensitiveBugReport>(
1715  BT_StreamEof,
1716  "Read function called when stream is in EOF state. "
1717  "Function has no effect.",
1718  N);
1719  R->markInteresting(StreamSym);
1720  C.emitReport(std::move(R));
1721  return;
1722  }
1723  C.addTransition(State);
1724 }
1725 
1726 ExplodedNode *
1727 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1728  CheckerContext &C, ExplodedNode *Pred) const {
1729  ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1730  if (!Err)
1731  return Pred;
1732 
1733  for (SymbolRef LeakSym : LeakedSyms) {
1734  // Resource leaks can result in multiple warning that describe the same kind
1735  // of programming error:
1736  // void f() {
1737  // FILE *F = fopen("a.txt");
1738  // if (rand()) // state split
1739  // return; // warning
1740  // } // warning
1741  // While this isn't necessarily true (leaking the same stream could result
1742  // from a different kinds of errors), the reduction in redundant reports
1743  // makes this a worthwhile heuristic.
1744  // FIXME: Add a checker option to turn this uniqueing feature off.
1745  const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1746  assert(StreamOpenNode && "Could not find place of stream opening.");
1747 
1748  PathDiagnosticLocation LocUsedForUniqueing;
1749  if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1750  LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1751  StreamStmt, C.getSourceManager(),
1752  StreamOpenNode->getLocationContext());
1753 
1754  std::unique_ptr<PathSensitiveBugReport> R =
1755  std::make_unique<PathSensitiveBugReport>(
1756  BT_ResourceLeak,
1757  "Opened stream never closed. Potential resource leak.", Err,
1758  LocUsedForUniqueing,
1759  StreamOpenNode->getLocationContext()->getDecl());
1760  R->markInteresting(LeakSym);
1761  C.emitReport(std::move(R));
1762  }
1763 
1764  return Err;
1765 }
1766 
1767 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1768  CheckerContext &C) const {
1769  ProgramStateRef State = C.getState();
1770 
1772 
1773  const StreamMapTy &Map = State->get<StreamMap>();
1774  for (const auto &I : Map) {
1775  SymbolRef Sym = I.first;
1776  const StreamState &SS = I.second;
1777  if (!SymReaper.isDead(Sym))
1778  continue;
1779  if (SS.isOpened())
1780  LeakedSyms.push_back(Sym);
1781  State = State->remove<StreamMap>(Sym);
1782  }
1783 
1784  ExplodedNode *N = C.getPredecessor();
1785  if (!LeakedSyms.empty())
1786  N = reportLeaks(LeakedSyms, C, N);
1787 
1788  C.addTransition(State, N);
1789 }
1790 
1791 ProgramStateRef StreamChecker::checkPointerEscape(
1792  ProgramStateRef State, const InvalidatedSymbols &Escaped,
1793  const CallEvent *Call, PointerEscapeKind Kind) const {
1794  // Check for file-handling system call that is not handled by the checker.
1795  // FIXME: The checker should be updated to handle all system calls that take
1796  // 'FILE*' argument. These are now ignored.
1797  if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
1798  return State;
1799 
1800  for (SymbolRef Sym : Escaped) {
1801  // The symbol escaped.
1802  // From now the stream can be manipulated in unknown way to the checker,
1803  // it is not possible to handle it any more.
1804  // Optimistically, assume that the corresponding file handle will be closed
1805  // somewhere else.
1806  // Remove symbol from state so the following stream calls on this symbol are
1807  // not handled by the checker.
1808  State = State->remove<StreamMap>(Sym);
1809  }
1810  return State;
1811 }
1812 
1813 //===----------------------------------------------------------------------===//
1814 // Checker registration.
1815 //===----------------------------------------------------------------------===//
1816 
1817 void ento::registerStreamChecker(CheckerManager &Mgr) {
1818  auto *Checker = Mgr.registerChecker<StreamChecker>();
1819  Checker->PedanticMode =
1821 }
1822 
1823 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1824  return true;
1825 }
1826 
1827 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1828  auto *Checker = Mgr.getChecker<StreamChecker>();
1829  Checker->TestMode = true;
1830 }
1831 
1832 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1833  return true;
1834 }
StringRef P
static char ID
Definition: Arena.cpp:183
#define X(type, name)
Definition: Value.h:143
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, ArrayRef< unsigned int > EscapingArgs)
LineState State
__DEVICE__ int max(int __a, int __b)
__device__ int
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2872
QualType getCallReturnType(const ASTContext &Ctx) const
getCallReturnType - Get the return type of the call expr.
Definition: Expr.cpp:1645
This represents one expression.
Definition: Expr.h:110
QualType getType() const
Definition: Expr.h:142
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
A (possibly-)qualified type.
Definition: Type.h:940
Stmt - This represents one statement.
Definition: Stmt.h:84
bool isPointerType() const
Definition: Type.h:7624
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Definition: Type.h:8054
const BugType & getBugType() const
Definition: BugReporter.h:149
An immutable map from CallDescriptions to arbitrary data.
const T * lookup(const CallEvent &Call) const
@ CLibrary
Match calls to functions from the C standard library.
@ SimpleFunc
Matches "simple" functions that are not methods.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
std::pair< ProgramStateRef, ProgramStateRef > ProgramStatePair
ExplodedNode * getFirstPred()
const ProgramStateRef & getState() const
const LocationContext * getLocationContext() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
void markNotInteresting(SymbolRef sym)
bool isInteresting(SymbolRef sym) const
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
Definition: SValBuilder.cpp:62
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
Definition: SValBuilder.h:290
virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0
Create a new value which represents a binary expression with two non- location operands.
QualType getConditionType() const
Definition: SValBuilder.h:153
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82
Symbolic value.
Definition: SymExpr.h:30
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
Value representing integer constant.
Definition: SVals.h:297
__inline void unsigned int _2
Definition: larchintrin.h:181
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)
std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)
Try to parse the value of a defined preprocessor macro.
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
bool Init(InterpState &S, CodePtr OpPC)
Definition: Interp.h:1472
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
BinaryOperatorKind
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:223
DiagnosticLevelMask operator&(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)
DiagnosticLevelMask operator~(DiagnosticLevelMask M)
DiagnosticLevelMask operator|(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)
bool operator!=(CanQual< T > x, CanQual< U > y)
const FunctionProtoType * T
unsigned long uint64_t
long int64_t
#define bool
Definition: stdbool.h:24