[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / StreamChecker.cpp
blob898906977ba9bb6e276ef1a7aca9a6494fd8149f
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.
11 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
24 #include <functional>
25 #include <optional>
27 using namespace clang;
28 using namespace ento;
29 using namespace std::placeholders;
31 //===----------------------------------------------------------------------===//
32 // Definition of state data structures.
33 //===----------------------------------------------------------------------===//
35 namespace {
37 struct FnDescription;
39 /// State of the stream error flags.
40 /// Sometimes it is not known to the checker what error flags are set.
41 /// This is indicated by setting more than one flag to true.
42 /// This is an optimization to avoid state splits.
43 /// A stream can either be in FEOF or FERROR but not both at the same time.
44 /// Multiple flags are set to handle the corresponding states together.
45 struct StreamErrorState {
46 /// The stream can be in state where none of the error flags set.
47 bool NoError = true;
48 /// The stream can be in state where the EOF indicator is set.
49 bool FEof = false;
50 /// The stream can be in state where the error indicator is set.
51 bool FError = false;
53 bool isNoError() const { return NoError && !FEof && !FError; }
54 bool isFEof() const { return !NoError && FEof && !FError; }
55 bool isFError() const { return !NoError && !FEof && FError; }
57 bool operator==(const StreamErrorState &ES) const {
58 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
61 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
63 StreamErrorState operator|(const StreamErrorState &E) const {
64 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
67 StreamErrorState operator&(const StreamErrorState &E) const {
68 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
71 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
73 /// Returns if the StreamErrorState is a valid object.
74 operator bool() const { return NoError || FEof || FError; }
76 void Profile(llvm::FoldingSetNodeID &ID) const {
77 ID.AddBoolean(NoError);
78 ID.AddBoolean(FEof);
79 ID.AddBoolean(FError);
83 const StreamErrorState ErrorNone{true, false, false};
84 const StreamErrorState ErrorFEof{false, true, false};
85 const StreamErrorState ErrorFError{false, false, true};
87 /// Full state information about a stream pointer.
88 struct StreamState {
89 /// The last file operation called in the stream.
90 /// Can be nullptr.
91 const FnDescription *LastOperation;
93 /// State of a stream symbol.
94 enum KindTy {
95 Opened, /// Stream is opened.
96 Closed, /// Closed stream (an invalid stream pointer after it was closed).
97 OpenFailed /// The last open operation has failed.
98 } State;
100 /// State of the error flags.
101 /// Ignored in non-opened stream state but must be NoError.
102 StreamErrorState const ErrorState;
104 /// Indicate if the file has an "indeterminate file position indicator".
105 /// This can be set at a failing read or write or seek operation.
106 /// If it is set no more read or write is allowed.
107 /// This value is not dependent on the stream error flags:
108 /// The error flag may be cleared with `clearerr` but the file position
109 /// remains still indeterminate.
110 /// This value applies to all error states in ErrorState except FEOF.
111 /// An EOF+indeterminate state is the same as EOF state.
112 bool const FilePositionIndeterminate = false;
114 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
115 bool IsFilePositionIndeterminate)
116 : LastOperation(L), State(S), ErrorState(ES),
117 FilePositionIndeterminate(IsFilePositionIndeterminate) {
118 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
119 "FilePositionIndeterminate should be false in FEof case.");
120 assert((State == Opened || ErrorState.isNoError()) &&
121 "ErrorState should be None in non-opened stream state.");
124 bool isOpened() const { return State == Opened; }
125 bool isClosed() const { return State == Closed; }
126 bool isOpenFailed() const { return State == OpenFailed; }
128 bool operator==(const StreamState &X) const {
129 // In not opened state error state should always NoError, so comparison
130 // here is no problem.
131 return LastOperation == X.LastOperation && State == X.State &&
132 ErrorState == X.ErrorState &&
133 FilePositionIndeterminate == X.FilePositionIndeterminate;
136 static StreamState getOpened(const FnDescription *L,
137 const StreamErrorState &ES = ErrorNone,
138 bool IsFilePositionIndeterminate = false) {
139 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
141 static StreamState getClosed(const FnDescription *L) {
142 return StreamState{L, Closed, {}, false};
144 static StreamState getOpenFailed(const FnDescription *L) {
145 return StreamState{L, OpenFailed, {}, false};
148 void Profile(llvm::FoldingSetNodeID &ID) const {
149 ID.AddPointer(LastOperation);
150 ID.AddInteger(State);
151 ErrorState.Profile(ID);
152 ID.AddBoolean(FilePositionIndeterminate);
156 } // namespace
158 //===----------------------------------------------------------------------===//
159 // StreamChecker class and utility functions.
160 //===----------------------------------------------------------------------===//
162 namespace {
164 class StreamChecker;
165 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
166 const CallEvent &, CheckerContext &)>;
168 using ArgNoTy = unsigned int;
169 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
171 struct FnDescription {
172 FnCheck PreFn;
173 FnCheck EvalFn;
174 ArgNoTy StreamArgNo;
177 /// Get the value of the stream argument out of the passed call event.
178 /// The call should contain a function that is described by Desc.
179 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
180 assert(Desc && Desc->StreamArgNo != ArgNone &&
181 "Try to get a non-existing stream argument.");
182 return Call.getArgSVal(Desc->StreamArgNo);
185 /// Create a conjured symbol return value for a call expression.
186 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
187 assert(CE && "Expecting a call expression.");
189 const LocationContext *LCtx = C.getLocationContext();
190 return C.getSValBuilder()
191 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
192 .castAs<DefinedSVal>();
195 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
196 const CallExpr *CE) {
197 DefinedSVal RetVal = makeRetVal(C, CE);
198 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
199 State = State->assume(RetVal, true);
200 assert(State && "Assumption on new value should not fail.");
201 return State;
204 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
205 CheckerContext &C, const CallExpr *CE) {
206 State = State->BindExpr(CE, C.getLocationContext(),
207 C.getSValBuilder().makeIntVal(Value, CE->getType()));
208 return State;
211 class StreamChecker : public Checker<check::PreCall, eval::Call,
212 check::DeadSymbols, check::PointerEscape> {
213 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
214 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
215 BugType BT_UseAfterOpenFailed{this, "Invalid stream",
216 "Stream handling error"};
217 BugType BT_IndeterminatePosition{this, "Invalid stream state",
218 "Stream handling error"};
219 BugType BT_IllegalWhence{this, "Illegal whence argument",
220 "Stream handling error"};
221 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
222 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
223 /*SuppressOnSink =*/true};
225 public:
226 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
227 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
228 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
229 ProgramStateRef checkPointerEscape(ProgramStateRef State,
230 const InvalidatedSymbols &Escaped,
231 const CallEvent *Call,
232 PointerEscapeKind Kind) const;
234 /// If true, evaluate special testing stream functions.
235 bool TestMode = false;
237 const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
239 private:
240 CallDescriptionMap<FnDescription> FnDescriptions = {
241 {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
242 {{{"freopen"}, 3},
243 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
244 {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
245 {{{"fclose"}, 1},
246 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
247 {{{"fread"}, 4},
248 {std::bind(&StreamChecker::preFreadFwrite, _1, _2, _3, _4, true),
249 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
250 {{{"fwrite"}, 4},
251 {std::bind(&StreamChecker::preFreadFwrite, _1, _2, _3, _4, false),
252 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
253 {{{"fseek"}, 3},
254 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
255 {{{"ftell"}, 1},
256 {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
257 {{{"rewind"}, 1},
258 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
259 {{{"fgetpos"}, 2},
260 {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
261 {{{"fsetpos"}, 2},
262 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
263 {{{"clearerr"}, 1},
264 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
265 {{{"feof"}, 1},
266 {&StreamChecker::preDefault,
267 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
268 0}},
269 {{{"ferror"}, 1},
270 {&StreamChecker::preDefault,
271 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
272 0}},
273 {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
276 CallDescriptionMap<FnDescription> FnTestDescriptions = {
277 {{{"StreamTesterChecker_make_feof_stream"}, 1},
278 {nullptr,
279 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
280 0}},
281 {{{"StreamTesterChecker_make_ferror_stream"}, 1},
282 {nullptr,
283 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
284 ErrorFError),
285 0}},
288 /// Expanded value of EOF, empty before initialization.
289 mutable std::optional<int> EofVal;
290 /// Expanded value of SEEK_SET, 0 if not found.
291 mutable int SeekSetVal = 0;
292 /// Expanded value of SEEK_CUR, 1 if not found.
293 mutable int SeekCurVal = 1;
294 /// Expanded value of SEEK_END, 2 if not found.
295 mutable int SeekEndVal = 2;
297 void evalFopen(const FnDescription *Desc, const CallEvent &Call,
298 CheckerContext &C) const;
300 void preFreopen(const FnDescription *Desc, const CallEvent &Call,
301 CheckerContext &C) const;
302 void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
303 CheckerContext &C) const;
305 void evalFclose(const FnDescription *Desc, const CallEvent &Call,
306 CheckerContext &C) const;
308 void preFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
309 CheckerContext &C, bool IsFread) const;
311 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
312 CheckerContext &C, bool IsFread) const;
314 void preFseek(const FnDescription *Desc, const CallEvent &Call,
315 CheckerContext &C) const;
316 void evalFseek(const FnDescription *Desc, const CallEvent &Call,
317 CheckerContext &C) const;
319 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
320 CheckerContext &C) const;
322 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
323 CheckerContext &C) const;
325 void evalFtell(const FnDescription *Desc, const CallEvent &Call,
326 CheckerContext &C) const;
328 void evalRewind(const FnDescription *Desc, const CallEvent &Call,
329 CheckerContext &C) const;
331 void preDefault(const FnDescription *Desc, const CallEvent &Call,
332 CheckerContext &C) const;
334 void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
335 CheckerContext &C) const;
337 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
338 CheckerContext &C,
339 const StreamErrorState &ErrorKind) const;
341 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
342 CheckerContext &C,
343 const StreamErrorState &ErrorKind) const;
345 /// Check that the stream (in StreamVal) is not NULL.
346 /// If it can only be NULL a fatal error is emitted and nullptr returned.
347 /// Otherwise the return value is a new state where the stream is constrained
348 /// to be non-null.
349 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
350 CheckerContext &C,
351 ProgramStateRef State) const;
353 /// Check that the stream is the opened state.
354 /// If the stream is known to be not opened an error is generated
355 /// and nullptr returned, otherwise the original state is returned.
356 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
357 ProgramStateRef State) const;
359 /// Check that the stream has not an invalid ("indeterminate") file position,
360 /// generate warning for it.
361 /// (EOF is not an invalid position.)
362 /// The returned state can be nullptr if a fatal error was generated.
363 /// It can return non-null state if the stream has not an invalid position or
364 /// there is execution path with non-invalid position.
365 ProgramStateRef
366 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
367 ProgramStateRef State) const;
369 /// Check the legality of the 'whence' argument of 'fseek'.
370 /// Generate error and return nullptr if it is found to be illegal.
371 /// Otherwise returns the state.
372 /// (State is not changed here because the "whence" value is already known.)
373 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
374 ProgramStateRef State) const;
376 /// Generate warning about stream in EOF state.
377 /// There will be always a state transition into the passed State,
378 /// by the new non-fatal error node or (if failed) a normal transition,
379 /// to ensure uniform handling.
380 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
381 ProgramStateRef State) const;
383 /// Emit resource leak warnings for the given symbols.
384 /// Createn a non-fatal error node for these, and returns it (if any warnings
385 /// were generated). Return value is non-null.
386 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
387 CheckerContext &C, ExplodedNode *Pred) const;
389 /// Find the description data of the function called by a call event.
390 /// Returns nullptr if no function is recognized.
391 const FnDescription *lookupFn(const CallEvent &Call) const {
392 // Recognize "global C functions" with only integral or pointer arguments
393 // (and matching name) as stream functions.
394 if (!Call.isGlobalCFunction())
395 return nullptr;
396 for (auto *P : Call.parameters()) {
397 QualType T = P->getType();
398 if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
399 return nullptr;
402 return FnDescriptions.lookup(Call);
405 /// Generate a message for BugReporterVisitor if the stored symbol is
406 /// marked as interesting by the actual bug report.
407 const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
408 const std::string &Message) const {
409 return C.getNoteTag([this, StreamSym,
410 Message](PathSensitiveBugReport &BR) -> std::string {
411 if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
412 return Message;
413 return "";
417 const NoteTag *constructSetEofNoteTag(CheckerContext &C,
418 SymbolRef StreamSym) const {
419 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
420 if (!BR.isInteresting(StreamSym) ||
421 &BR.getBugType() != this->getBT_StreamEof())
422 return "";
424 BR.markNotInteresting(StreamSym);
426 return "Assuming stream reaches end-of-file here";
430 void initMacroValues(CheckerContext &C) const {
431 if (EofVal)
432 return;
434 if (const std::optional<int> OptInt =
435 tryExpandAsInteger("EOF", C.getPreprocessor()))
436 EofVal = *OptInt;
437 else
438 EofVal = -1;
439 if (const std::optional<int> OptInt =
440 tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
441 SeekSetVal = *OptInt;
442 if (const std::optional<int> OptInt =
443 tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
444 SeekEndVal = *OptInt;
445 if (const std::optional<int> OptInt =
446 tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
447 SeekCurVal = *OptInt;
450 /// Searches for the ExplodedNode where the file descriptor was acquired for
451 /// StreamSym.
452 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
453 SymbolRef StreamSym,
454 CheckerContext &C);
457 } // end anonymous namespace
459 // This map holds the state of a stream.
460 // The stream is identified with a SymbolRef that is created when a stream
461 // opening function is modeled by the checker.
462 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
464 inline void assertStreamStateOpened(const StreamState *SS) {
465 assert(SS->isOpened() && "Stream is expected to be opened");
468 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
469 SymbolRef StreamSym,
470 CheckerContext &C) {
471 ProgramStateRef State = N->getState();
472 // When bug type is resource leak, exploded node N may not have state info
473 // for leaked file descriptor, but predecessor should have it.
474 if (!State->get<StreamMap>(StreamSym))
475 N = N->getFirstPred();
477 const ExplodedNode *Pred = N;
478 while (N) {
479 State = N->getState();
480 if (!State->get<StreamMap>(StreamSym))
481 return Pred;
482 Pred = N;
483 N = N->getFirstPred();
486 return nullptr;
489 //===----------------------------------------------------------------------===//
490 // Methods of StreamChecker.
491 //===----------------------------------------------------------------------===//
493 void StreamChecker::checkPreCall(const CallEvent &Call,
494 CheckerContext &C) const {
495 initMacroValues(C);
497 const FnDescription *Desc = lookupFn(Call);
498 if (!Desc || !Desc->PreFn)
499 return;
501 Desc->PreFn(this, Desc, Call, C);
504 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
505 const FnDescription *Desc = lookupFn(Call);
506 if (!Desc && TestMode)
507 Desc = FnTestDescriptions.lookup(Call);
508 if (!Desc || !Desc->EvalFn)
509 return false;
511 Desc->EvalFn(this, Desc, Call, C);
513 return C.isDifferent();
516 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
517 CheckerContext &C) const {
518 ProgramStateRef State = C.getState();
519 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
520 if (!CE)
521 return;
523 DefinedSVal RetVal = makeRetVal(C, CE);
524 SymbolRef RetSym = RetVal.getAsSymbol();
525 assert(RetSym && "RetVal must be a symbol here.");
527 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
529 // Bifurcate the state into two: one with a valid FILE* pointer, the other
530 // with a NULL.
531 ProgramStateRef StateNotNull, StateNull;
532 std::tie(StateNotNull, StateNull) =
533 C.getConstraintManager().assumeDual(State, RetVal);
535 StateNotNull =
536 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
537 StateNull =
538 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
540 C.addTransition(StateNotNull,
541 constructNoteTag(C, RetSym, "Stream opened here"));
542 C.addTransition(StateNull);
545 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
546 CheckerContext &C) const {
547 // Do not allow NULL as passed stream pointer but allow a closed stream.
548 ProgramStateRef State = C.getState();
549 State = ensureStreamNonNull(getStreamArg(Desc, Call),
550 Call.getArgExpr(Desc->StreamArgNo), C, State);
551 if (!State)
552 return;
554 C.addTransition(State);
557 void StreamChecker::evalFreopen(const FnDescription *Desc,
558 const CallEvent &Call,
559 CheckerContext &C) const {
560 ProgramStateRef State = C.getState();
562 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
563 if (!CE)
564 return;
566 std::optional<DefinedSVal> StreamVal =
567 getStreamArg(Desc, Call).getAs<DefinedSVal>();
568 if (!StreamVal)
569 return;
571 SymbolRef StreamSym = StreamVal->getAsSymbol();
572 // Do not care about concrete values for stream ("(FILE *)0x12345"?).
573 // FIXME: Can be stdin, stdout, stderr such values?
574 if (!StreamSym)
575 return;
577 // Do not handle untracked stream. It is probably escaped.
578 if (!State->get<StreamMap>(StreamSym))
579 return;
581 // Generate state for non-failed case.
582 // Return value is the passed stream pointer.
583 // According to the documentations, the stream is closed first
584 // but any close error is ignored. The state changes to (or remains) opened.
585 ProgramStateRef StateRetNotNull =
586 State->BindExpr(CE, C.getLocationContext(), *StreamVal);
587 // Generate state for NULL return value.
588 // Stream switches to OpenFailed state.
589 ProgramStateRef StateRetNull =
590 State->BindExpr(CE, C.getLocationContext(),
591 C.getSValBuilder().makeNullWithType(CE->getType()));
593 StateRetNotNull =
594 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
595 StateRetNull =
596 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
598 C.addTransition(StateRetNotNull,
599 constructNoteTag(C, StreamSym, "Stream reopened here"));
600 C.addTransition(StateRetNull);
603 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
604 CheckerContext &C) const {
605 ProgramStateRef State = C.getState();
606 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
607 if (!Sym)
608 return;
610 const StreamState *SS = State->get<StreamMap>(Sym);
611 if (!SS)
612 return;
614 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
615 if (!CE)
616 return;
618 assertStreamStateOpened(SS);
620 // Close the File Descriptor.
621 // Regardless if the close fails or not, stream becomes "closed"
622 // and can not be used any more.
623 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
625 // Return 0 on success, EOF on failure.
626 SValBuilder &SVB = C.getSValBuilder();
627 ProgramStateRef StateSuccess = State->BindExpr(
628 CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
629 ProgramStateRef StateFailure =
630 State->BindExpr(CE, C.getLocationContext(),
631 SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
633 C.addTransition(StateSuccess);
634 C.addTransition(StateFailure);
637 void StreamChecker::preFreadFwrite(const FnDescription *Desc,
638 const CallEvent &Call, CheckerContext &C,
639 bool IsFread) const {
640 ProgramStateRef State = C.getState();
641 SVal StreamVal = getStreamArg(Desc, Call);
642 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
643 State);
644 if (!State)
645 return;
646 State = ensureStreamOpened(StreamVal, C, State);
647 if (!State)
648 return;
649 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
650 if (!State)
651 return;
653 if (!IsFread) {
654 C.addTransition(State);
655 return;
658 SymbolRef Sym = StreamVal.getAsSymbol();
659 if (Sym && State->get<StreamMap>(Sym)) {
660 const StreamState *SS = State->get<StreamMap>(Sym);
661 if (SS->ErrorState & ErrorFEof)
662 reportFEofWarning(Sym, C, State);
663 } else {
664 C.addTransition(State);
668 void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
669 const CallEvent &Call, CheckerContext &C,
670 bool IsFread) const {
671 ProgramStateRef State = C.getState();
672 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
673 if (!StreamSym)
674 return;
676 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
677 if (!CE)
678 return;
680 std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
681 if (!SizeVal)
682 return;
683 std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
684 if (!NMembVal)
685 return;
687 const StreamState *OldSS = State->get<StreamMap>(StreamSym);
688 if (!OldSS)
689 return;
691 assertStreamStateOpened(OldSS);
693 // C'99 standard, §7.19.8.1.3, the return value of fread:
694 // The fread function returns the number of elements successfully read, which
695 // may be less than nmemb if a read error or end-of-file is encountered. If
696 // size or nmemb is zero, fread returns zero and the contents of the array and
697 // the state of the stream remain unchanged.
699 if (State->isNull(*SizeVal).isConstrainedTrue() ||
700 State->isNull(*NMembVal).isConstrainedTrue()) {
701 // This is the "size or nmemb is zero" case.
702 // Just return 0, do nothing more (not clear the error flags).
703 State = bindInt(0, State, C, CE);
704 C.addTransition(State);
705 return;
708 // Generate a transition for the success state.
709 // If we know the state to be FEOF at fread, do not add a success state.
710 if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
711 ProgramStateRef StateNotFailed =
712 State->BindExpr(CE, C.getLocationContext(), *NMembVal);
713 StateNotFailed =
714 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
715 C.addTransition(StateNotFailed);
718 // Add transition for the failed state.
719 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
720 ProgramStateRef StateFailed =
721 State->BindExpr(CE, C.getLocationContext(), RetVal);
722 auto Cond =
723 C.getSValBuilder()
724 .evalBinOpNN(State, BO_LT, RetVal, *NMembVal, C.getASTContext().IntTy)
725 .getAs<DefinedOrUnknownSVal>();
726 if (!Cond)
727 return;
728 StateFailed = StateFailed->assume(*Cond, true);
729 if (!StateFailed)
730 return;
732 StreamErrorState NewES;
733 if (IsFread)
734 NewES =
735 (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
736 else
737 NewES = ErrorFError;
738 // If a (non-EOF) error occurs, the resulting value of the file position
739 // indicator for the stream is indeterminate.
740 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
741 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
742 if (IsFread && OldSS->ErrorState != ErrorFEof)
743 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
744 else
745 C.addTransition(StateFailed);
748 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
749 CheckerContext &C) const {
750 ProgramStateRef State = C.getState();
751 SVal StreamVal = getStreamArg(Desc, Call);
752 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
753 State);
754 if (!State)
755 return;
756 State = ensureStreamOpened(StreamVal, C, State);
757 if (!State)
758 return;
759 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
760 if (!State)
761 return;
763 C.addTransition(State);
766 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
767 CheckerContext &C) const {
768 ProgramStateRef State = C.getState();
769 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
770 if (!StreamSym)
771 return;
773 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
774 if (!CE)
775 return;
777 // Ignore the call if the stream is not tracked.
778 if (!State->get<StreamMap>(StreamSym))
779 return;
781 const llvm::APSInt *PosV =
782 C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
783 const llvm::APSInt *WhenceV =
784 C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
786 DefinedSVal RetVal = makeRetVal(C, CE);
788 // Make expression result.
789 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
791 // Bifurcate the state into failed and non-failed.
792 // Return zero on success, nonzero on error.
793 ProgramStateRef StateNotFailed, StateFailed;
794 std::tie(StateFailed, StateNotFailed) =
795 C.getConstraintManager().assumeDual(State, RetVal);
797 // Reset the state to opened with no error.
798 StateNotFailed =
799 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
800 // We get error.
801 // It is possible that fseek fails but sets none of the error flags.
802 // If fseek failed, assume that the file position becomes indeterminate in any
803 // case.
804 StreamErrorState NewErrS = ErrorNone | ErrorFError;
805 // Setting the position to start of file never produces EOF error.
806 if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
807 NewErrS = NewErrS | ErrorFEof;
808 StateFailed = StateFailed->set<StreamMap>(
809 StreamSym, StreamState::getOpened(Desc, NewErrS, true));
811 C.addTransition(StateNotFailed);
812 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
815 void StreamChecker::evalFgetpos(const FnDescription *Desc,
816 const CallEvent &Call,
817 CheckerContext &C) const {
818 ProgramStateRef State = C.getState();
819 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
820 if (!Sym)
821 return;
823 // Do not evaluate if stream is not found.
824 if (!State->get<StreamMap>(Sym))
825 return;
827 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
828 if (!CE)
829 return;
831 DefinedSVal RetVal = makeRetVal(C, CE);
832 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
833 ProgramStateRef StateNotFailed, StateFailed;
834 std::tie(StateFailed, StateNotFailed) =
835 C.getConstraintManager().assumeDual(State, RetVal);
837 // This function does not affect the stream state.
838 // Still we add success and failure state with the appropriate return value.
839 // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
840 C.addTransition(StateNotFailed);
841 C.addTransition(StateFailed);
844 void StreamChecker::evalFsetpos(const FnDescription *Desc,
845 const CallEvent &Call,
846 CheckerContext &C) const {
847 ProgramStateRef State = C.getState();
848 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
849 if (!StreamSym)
850 return;
852 const StreamState *SS = State->get<StreamMap>(StreamSym);
853 if (!SS)
854 return;
856 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
857 if (!CE)
858 return;
860 assertStreamStateOpened(SS);
862 DefinedSVal RetVal = makeRetVal(C, CE);
863 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
864 ProgramStateRef StateNotFailed, StateFailed;
865 std::tie(StateFailed, StateNotFailed) =
866 C.getConstraintManager().assumeDual(State, RetVal);
868 StateNotFailed = StateNotFailed->set<StreamMap>(
869 StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
871 // At failure ferror could be set.
872 // The standards do not tell what happens with the file position at failure.
873 // But we can assume that it is dangerous to make a next I/O operation after
874 // the position was not set correctly (similar to 'fseek').
875 StateFailed = StateFailed->set<StreamMap>(
876 StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
878 C.addTransition(StateNotFailed);
879 C.addTransition(StateFailed);
882 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
883 CheckerContext &C) const {
884 ProgramStateRef State = C.getState();
885 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
886 if (!Sym)
887 return;
889 if (!State->get<StreamMap>(Sym))
890 return;
892 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
893 if (!CE)
894 return;
896 SValBuilder &SVB = C.getSValBuilder();
897 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
898 ProgramStateRef StateNotFailed =
899 State->BindExpr(CE, C.getLocationContext(), RetVal);
900 auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
901 SVB.makeZeroVal(C.getASTContext().LongTy),
902 SVB.getConditionType())
903 .getAs<DefinedOrUnknownSVal>();
904 if (!Cond)
905 return;
906 StateNotFailed = StateNotFailed->assume(*Cond, true);
907 if (!StateNotFailed)
908 return;
910 ProgramStateRef StateFailed = State->BindExpr(
911 CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
913 C.addTransition(StateNotFailed);
914 C.addTransition(StateFailed);
917 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
918 CheckerContext &C) const {
919 ProgramStateRef State = C.getState();
920 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
921 if (!StreamSym)
922 return;
924 const StreamState *SS = State->get<StreamMap>(StreamSym);
925 if (!SS)
926 return;
928 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
929 if (!CE)
930 return;
932 assertStreamStateOpened(SS);
934 State = State->set<StreamMap>(StreamSym,
935 StreamState::getOpened(Desc, ErrorNone, false));
937 C.addTransition(State);
940 void StreamChecker::evalClearerr(const FnDescription *Desc,
941 const CallEvent &Call,
942 CheckerContext &C) const {
943 ProgramStateRef State = C.getState();
944 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
945 if (!StreamSym)
946 return;
948 const StreamState *SS = State->get<StreamMap>(StreamSym);
949 if (!SS)
950 return;
952 assertStreamStateOpened(SS);
954 // FilePositionIndeterminate is not cleared.
955 State = State->set<StreamMap>(
956 StreamSym,
957 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
958 C.addTransition(State);
961 void StreamChecker::evalFeofFerror(const FnDescription *Desc,
962 const CallEvent &Call, CheckerContext &C,
963 const StreamErrorState &ErrorKind) const {
964 ProgramStateRef State = C.getState();
965 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
966 if (!StreamSym)
967 return;
969 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
970 if (!CE)
971 return;
973 const StreamState *SS = State->get<StreamMap>(StreamSym);
974 if (!SS)
975 return;
977 assertStreamStateOpened(SS);
979 if (SS->ErrorState & ErrorKind) {
980 // Execution path with error of ErrorKind.
981 // Function returns true.
982 // From now on it is the only one error state.
983 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
984 C.addTransition(TrueState->set<StreamMap>(
985 StreamSym, StreamState::getOpened(Desc, ErrorKind,
986 SS->FilePositionIndeterminate &&
987 !ErrorKind.isFEof())));
989 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
990 // Execution path(s) with ErrorKind not set.
991 // Function returns false.
992 // New error state is everything before minus ErrorKind.
993 ProgramStateRef FalseState = bindInt(0, State, C, CE);
994 C.addTransition(FalseState->set<StreamMap>(
995 StreamSym,
996 StreamState::getOpened(
997 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
1001 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1002 CheckerContext &C) const {
1003 ProgramStateRef State = C.getState();
1004 SVal StreamVal = getStreamArg(Desc, Call);
1005 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1006 State);
1007 if (!State)
1008 return;
1009 State = ensureStreamOpened(StreamVal, C, State);
1010 if (!State)
1011 return;
1013 C.addTransition(State);
1016 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1017 const CallEvent &Call, CheckerContext &C,
1018 const StreamErrorState &ErrorKind) const {
1019 ProgramStateRef State = C.getState();
1020 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1021 assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1022 const StreamState *SS = State->get<StreamMap>(StreamSym);
1023 assert(SS && "Stream should be tracked by the checker.");
1024 State = State->set<StreamMap>(
1025 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
1026 C.addTransition(State);
1029 ProgramStateRef
1030 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1031 CheckerContext &C,
1032 ProgramStateRef State) const {
1033 auto Stream = StreamVal.getAs<DefinedSVal>();
1034 if (!Stream)
1035 return State;
1037 ConstraintManager &CM = C.getConstraintManager();
1039 ProgramStateRef StateNotNull, StateNull;
1040 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1042 if (!StateNotNull && StateNull) {
1043 if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1044 auto R = std::make_unique<PathSensitiveBugReport>(
1045 BT_FileNull, "Stream pointer might be NULL.", N);
1046 if (StreamE)
1047 bugreporter::trackExpressionValue(N, StreamE, *R);
1048 C.emitReport(std::move(R));
1050 return nullptr;
1053 return StateNotNull;
1056 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1057 CheckerContext &C,
1058 ProgramStateRef State) const {
1059 SymbolRef Sym = StreamVal.getAsSymbol();
1060 if (!Sym)
1061 return State;
1063 const StreamState *SS = State->get<StreamMap>(Sym);
1064 if (!SS)
1065 return State;
1067 if (SS->isClosed()) {
1068 // Using a stream pointer after 'fclose' causes undefined behavior
1069 // according to cppreference.com .
1070 ExplodedNode *N = C.generateErrorNode();
1071 if (N) {
1072 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1073 BT_UseAfterClose,
1074 "Stream might be already closed. Causes undefined behaviour.", N));
1075 return nullptr;
1078 return State;
1081 if (SS->isOpenFailed()) {
1082 // Using a stream that has failed to open is likely to cause problems.
1083 // This should usually not occur because stream pointer is NULL.
1084 // But freopen can cause a state when stream pointer remains non-null but
1085 // failed to open.
1086 ExplodedNode *N = C.generateErrorNode();
1087 if (N) {
1088 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1089 BT_UseAfterOpenFailed,
1090 "Stream might be invalid after "
1091 "(re-)opening it has failed. "
1092 "Can cause undefined behaviour.",
1093 N));
1094 return nullptr;
1096 return State;
1099 return State;
1102 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1103 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1104 static const char *BugMessage =
1105 "File position of the stream might be 'indeterminate' "
1106 "after a failed operation. "
1107 "Can cause undefined behavior.";
1109 SymbolRef Sym = StreamVal.getAsSymbol();
1110 if (!Sym)
1111 return State;
1113 const StreamState *SS = State->get<StreamMap>(Sym);
1114 if (!SS)
1115 return State;
1117 assert(SS->isOpened() && "First ensure that stream is opened.");
1119 if (SS->FilePositionIndeterminate) {
1120 if (SS->ErrorState & ErrorFEof) {
1121 // The error is unknown but may be FEOF.
1122 // Continue analysis with the FEOF error state.
1123 // Report warning because the other possible error states.
1124 ExplodedNode *N = C.generateNonFatalErrorNode(State);
1125 if (!N)
1126 return nullptr;
1128 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1129 BT_IndeterminatePosition, BugMessage, N));
1130 return State->set<StreamMap>(
1131 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1134 // Known or unknown error state without FEOF possible.
1135 // Stop analysis, report error.
1136 ExplodedNode *N = C.generateErrorNode(State);
1137 if (N)
1138 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1139 BT_IndeterminatePosition, BugMessage, N));
1141 return nullptr;
1144 return State;
1147 ProgramStateRef
1148 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1149 ProgramStateRef State) const {
1150 std::optional<nonloc::ConcreteInt> CI =
1151 WhenceVal.getAs<nonloc::ConcreteInt>();
1152 if (!CI)
1153 return State;
1155 int64_t X = CI->getValue().getSExtValue();
1156 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1157 return State;
1159 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1160 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1161 BT_IllegalWhence,
1162 "The whence argument to fseek() should be "
1163 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1164 N));
1165 return nullptr;
1168 return State;
1171 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1172 ProgramStateRef State) const {
1173 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1174 auto R = std::make_unique<PathSensitiveBugReport>(
1175 BT_StreamEof,
1176 "Read function called when stream is in EOF state. "
1177 "Function has no effect.",
1179 R->markInteresting(StreamSym);
1180 C.emitReport(std::move(R));
1181 return;
1183 C.addTransition(State);
1186 ExplodedNode *
1187 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1188 CheckerContext &C, ExplodedNode *Pred) const {
1189 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1190 if (!Err)
1191 return Pred;
1193 for (SymbolRef LeakSym : LeakedSyms) {
1194 // Resource leaks can result in multiple warning that describe the same kind
1195 // of programming error:
1196 // void f() {
1197 // FILE *F = fopen("a.txt");
1198 // if (rand()) // state split
1199 // return; // warning
1200 // } // warning
1201 // While this isn't necessarily true (leaking the same stream could result
1202 // from a different kinds of errors), the reduction in redundant reports
1203 // makes this a worthwhile heuristic.
1204 // FIXME: Add a checker option to turn this uniqueing feature off.
1205 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1206 assert(StreamOpenNode && "Could not find place of stream opening.");
1208 PathDiagnosticLocation LocUsedForUniqueing;
1209 if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1210 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1211 StreamStmt, C.getSourceManager(),
1212 StreamOpenNode->getLocationContext());
1214 std::unique_ptr<PathSensitiveBugReport> R =
1215 std::make_unique<PathSensitiveBugReport>(
1216 BT_ResourceLeak,
1217 "Opened stream never closed. Potential resource leak.", Err,
1218 LocUsedForUniqueing,
1219 StreamOpenNode->getLocationContext()->getDecl());
1220 R->markInteresting(LeakSym);
1221 C.emitReport(std::move(R));
1224 return Err;
1227 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1228 CheckerContext &C) const {
1229 ProgramStateRef State = C.getState();
1231 llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1233 const StreamMapTy &Map = State->get<StreamMap>();
1234 for (const auto &I : Map) {
1235 SymbolRef Sym = I.first;
1236 const StreamState &SS = I.second;
1237 if (!SymReaper.isDead(Sym))
1238 continue;
1239 if (SS.isOpened())
1240 LeakedSyms.push_back(Sym);
1241 State = State->remove<StreamMap>(Sym);
1244 ExplodedNode *N = C.getPredecessor();
1245 if (!LeakedSyms.empty())
1246 N = reportLeaks(LeakedSyms, C, N);
1248 C.addTransition(State, N);
1251 ProgramStateRef StreamChecker::checkPointerEscape(
1252 ProgramStateRef State, const InvalidatedSymbols &Escaped,
1253 const CallEvent *Call, PointerEscapeKind Kind) const {
1254 // Check for file-handling system call that is not handled by the checker.
1255 // FIXME: The checker should be updated to handle all system calls that take
1256 // 'FILE*' argument. These are now ignored.
1257 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
1258 return State;
1260 for (SymbolRef Sym : Escaped) {
1261 // The symbol escaped.
1262 // From now the stream can be manipulated in unknown way to the checker,
1263 // it is not possible to handle it any more.
1264 // Optimistically, assume that the corresponding file handle will be closed
1265 // somewhere else.
1266 // Remove symbol from state so the following stream calls on this symbol are
1267 // not handled by the checker.
1268 State = State->remove<StreamMap>(Sym);
1270 return State;
1273 //===----------------------------------------------------------------------===//
1274 // Checker registration.
1275 //===----------------------------------------------------------------------===//
1277 void ento::registerStreamChecker(CheckerManager &Mgr) {
1278 Mgr.registerChecker<StreamChecker>();
1281 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1282 return true;
1285 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1286 auto *Checker = Mgr.getChecker<StreamChecker>();
1287 Checker->TestMode = true;
1290 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1291 return true;