clang: lib/StaticAnalyzer/Checkers/StreamChecker.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
27#include "llvm/ADT/Sequence.h"
28#include
29#include
30
31using namespace clang;
32using namespace ento;
33using namespace std::placeholders;
34
35
36
37
38
39namespace {
40
41struct FnDescription;
42
43
44
45
46
47
48
49struct StreamErrorState {
50
51 bool NoError = true;
52
53 bool FEof = false;
54
55 bool FError = false;
56
57 bool isNoError() const { return NoError && !FEof && !FError; }
58 bool isFEof() const { return !NoError && FEof && !FError; }
59 bool isFError() const { return !NoError && !FEof && FError; }
60
61 bool operator==(const StreamErrorState &ES) const {
62 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
63 }
64
65 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
66
67 StreamErrorState operator|(const StreamErrorState &E) const {
68 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
69 }
70
71 StreamErrorState operator&(const StreamErrorState &E) const {
72 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
73 }
74
75 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
76
77
78 operator bool() const { return NoError || FEof || FError; }
79
80 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
81 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const {
82 os << "NoError: " << NoError << ", FEof: " << FEof
83 << ", FError: " << FError;
84 }
85
86 void Profile(llvm::FoldingSetNodeID &ID) const {
87 ID.AddBoolean(NoError);
88 ID.AddBoolean(FEof);
89 ID.AddBoolean(FError);
90 }
91};
92
93const StreamErrorState ErrorNone{true, false, false};
94const StreamErrorState ErrorFEof{false, true, false};
95const StreamErrorState ErrorFError{false, false, true};
96
97
98struct StreamState {
99
100
101 const FnDescription *LastOperation;
102
103
104 enum KindTy {
105 Opened,
106 Closed,
107 OpenFailed
108 } State;
109
110 StringRef getKindStr() const {
111 switch (State) {
112 case Opened:
113 return "Opened";
115 return "Closed";
116 case OpenFailed:
117 return "OpenFailed";
118 }
119 llvm_unreachable("Unknown StreamState!");
120 }
121
122
123
124 StreamErrorState const ErrorState;
125
126
127
128
129
130
131
132
133
134 bool const FilePositionIndeterminate = false;
135
136 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
137 bool IsFilePositionIndeterminate)
138 : LastOperation(L), State(S), ErrorState(ES),
139 FilePositionIndeterminate(IsFilePositionIndeterminate) {
140 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
141 "FilePositionIndeterminate should be false in FEof case.");
142 assert((State == Opened || ErrorState.isNoError()) &&
143 "ErrorState should be None in non-opened stream state.");
144 }
145
146 bool isOpened() const { return State == Opened; }
147 bool isClosed() const { return State == Closed; }
148 bool isOpenFailed() const { return State == OpenFailed; }
149
150 bool operator==(const StreamState &X) const {
151
152
153 return LastOperation == X.LastOperation && State == X.State &&
154 ErrorState == X.ErrorState &&
155 FilePositionIndeterminate == X.FilePositionIndeterminate;
156 }
157
158 static StreamState getOpened(const FnDescription *L,
159 const StreamErrorState &ES = ErrorNone,
160 bool IsFilePositionIndeterminate = false) {
161 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
162 }
163 static StreamState getClosed(const FnDescription *L) {
164 return StreamState{L, Closed, {}, false};
165 }
166 static StreamState getOpenFailed(const FnDescription *L) {
167 return StreamState{L, OpenFailed, {}, false};
168 }
169
170 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
171 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const;
172
173 void Profile(llvm::FoldingSetNodeID &ID) const {
174 ID.AddPointer(LastOperation);
175 ID.AddInteger(State);
176 ErrorState.Profile(ID);
177 ID.AddBoolean(FilePositionIndeterminate);
178 }
179};
180
181}
182
183
184
185
187
188
189
190
191
192namespace {
193
194class StreamChecker;
195using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
197
198using ArgNoTy = unsigned int;
199static const ArgNoTy ArgNone = std::numeric_limits::max();
200
201const char *FeofNote = "Assuming stream reaches end-of-file here";
202const char *FerrorNote = "Assuming this stream operation fails";
203
204struct FnDescription {
205 FnCheck PreFn;
206 FnCheck EvalFn;
207 ArgNoTy StreamArgNo;
208};
209
210LLVM_DUMP_METHOD void StreamState::dumpToStream(llvm::raw_ostream &os) const {
211 os << "{Kind: " << getKindStr() << ", Last operation: " << LastOperation
212 << ", ErrorState: ";
213 ErrorState.dumpToStream(os);
214 os << ", FilePos: " << (FilePositionIndeterminate ? "Indeterminate" : "OK")
215 << '}';
216}
217
218
219
220SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
221 assert(Desc && Desc->StreamArgNo != ArgNone &&
222 "Try to get a non-existing stream argument.");
223 return Call.getArgSVal(Desc->StreamArgNo);
224}
225
226
228 assert(CE && "Expecting a call expression.");
229
231 return C.getSValBuilder()
232 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
234}
235
239 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
240 State = State->assume(RetVal, true);
241 assert(State && "Assumption on new value should not fail.");
242 return State;
243}
244
247 State = State->BindExpr(CE, C.getLocationContext(),
248 C.getSValBuilder().makeIntVal(Value, CE->getType()));
249 return State;
250}
251
252inline void assertStreamStateOpened(const StreamState *SS) {
253 assert(SS->isOpened() && "Stream is expected to be opened");
254}
255
256class StreamChecker : public Checker<check::PreCall, eval::Call,
257 check::DeadSymbols, check::PointerEscape,
258 check::ASTDecl> {
259 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
260 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
261 BugType BT_UseAfterOpenFailed{this, "Invalid stream",
262 "Stream handling error"};
263 BugType BT_IndeterminatePosition{this, "Invalid stream state",
264 "Stream handling error"};
265 BugType BT_IllegalWhence{this, "Illegal whence argument",
266 "Stream handling error"};
267 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
268 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
269 true};
270
271public:
279
280
283
284 const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
285 const BugType *getBT_IndeterminatePosition() const {
286 return &BT_IndeterminatePosition;
287 }
288
289
290
294
299 &BR.getBugType() != this->getBT_StreamEof())
300 return "";
301
303
304 return FeofNote;
305 });
306 }
307
312 &BR.getBugType() != this->getBT_IndeterminatePosition())
313 return "";
314
316
317 return FerrorNote;
318 });
319 }
320
325 return "";
326
327 if (&BR.getBugType() == this->getBT_StreamEof()) {
328 BR.markNotInteresting(StreamSym);
329 return FeofNote;
330 }
331 if (&BR.getBugType() == this->getBT_IndeterminatePosition()) {
332 BR.markNotInteresting(StreamSym);
333 return FerrorNote;
334 }
335
336 return "";
337 });
338 }
339
340
341 bool TestMode = false;
342
343
344 bool PedanticMode = false;
345
346 const CallDescription FCloseDesc = {CDM::CLibrary, {"fclose"}, 1};
347
348private:
350 {{CDM::CLibrary, {"fopen"}, 2},
351 {nullptr, &StreamChecker::evalFopen, ArgNone}},
352 {{CDM::CLibrary, {"fdopen"}, 2},
353 {nullptr, &StreamChecker::evalFopen, ArgNone}},
354 {{CDM::CLibrary, {"freopen"}, 3},
355 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
356 {{CDM::CLibrary, {"tmpfile"}, 0},
357 {nullptr, &StreamChecker::evalFopen, ArgNone}},
358 {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
359 {{CDM::CLibrary, {"fread"}, 4},
360 {&StreamChecker::preRead,
361 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
362 {{CDM::CLibrary, {"fwrite"}, 4},
363 {&StreamChecker::preWrite,
364 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
365 {{CDM::CLibrary, {"fgetc"}, 1},
366 {&StreamChecker::preRead,
367 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
368 {{CDM::CLibrary, {"fgets"}, 3},
369 {&StreamChecker::preRead,
370 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
371 {{CDM::CLibrary, {"getc"}, 1},
372 {&StreamChecker::preRead,
373 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
374 {{CDM::CLibrary, {"fputc"}, 2},
375 {&StreamChecker::preWrite,
376 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
377 {{CDM::CLibrary, {"fputs"}, 2},
378 {&StreamChecker::preWrite,
379 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
380 {{CDM::CLibrary, {"putc"}, 2},
381 {&StreamChecker::preWrite,
382 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
383 {{CDM::CLibrary, {"fprintf"}},
384 {&StreamChecker::preWrite,
385 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
386 {{CDM::CLibrary, {"vfprintf"}, 3},
387 {&StreamChecker::preWrite,
388 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
389 {{CDM::CLibrary, {"fscanf"}},
390 {&StreamChecker::preRead,
391 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
392 {{CDM::CLibrary, {"vfscanf"}, 3},
393 {&StreamChecker::preRead,
394 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
395 {{CDM::CLibrary, {"ungetc"}, 2},
396 {&StreamChecker::preWrite,
397 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
398 {{CDM::CLibrary, {"getdelim"}, 4},
399 {&StreamChecker::preRead,
400 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
401 {{CDM::CLibrary, {"getline"}, 3},
402 {&StreamChecker::preRead,
403 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
404 {{CDM::CLibrary, {"fseek"}, 3},
405 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
406 {{CDM::CLibrary, {"fseeko"}, 3},
407 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
408 {{CDM::CLibrary, {"ftell"}, 1},
409 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
410 {{CDM::CLibrary, {"ftello"}, 1},
411 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
412 {{CDM::CLibrary, {"fflush"}, 1},
413 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
414 {{CDM::CLibrary, {"rewind"}, 1},
415 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
416 {{CDM::CLibrary, {"fgetpos"}, 2},
417 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
418 {{CDM::CLibrary, {"fsetpos"}, 2},
419 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
420 {{CDM::CLibrary, {"clearerr"}, 1},
421 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
422 {{CDM::CLibrary, {"feof"}, 1},
423 {&StreamChecker::preDefault,
424 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
425 0}},
426 {{CDM::CLibrary, {"ferror"}, 1},
427 {&StreamChecker::preDefault,
428 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
429 0}},
430 {{CDM::CLibrary, {"fileno"}, 1},
431 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
432 };
433
435 {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},
436 {nullptr,
437 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
438 false),
439 0}},
440 {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},
441 {nullptr,
442 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
443 ErrorFError, false),
444 0}},
445 {{CDM::SimpleFunc,
446 {"StreamTesterChecker_make_ferror_indeterminate_stream"},
447 1},
448 {nullptr,
449 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
450 ErrorFError, true),
451 0}},
452 };
453
454
455 mutable std::optional EofVal;
456
457 mutable int SeekSetVal = 0;
458
459 mutable int SeekCurVal = 1;
460
461 mutable int SeekEndVal = 2;
462
464
465 mutable const VarDecl *StdinDecl = nullptr;
466 mutable const VarDecl *StdoutDecl = nullptr;
467 mutable const VarDecl *StderrDecl = nullptr;
468
469 void evalFopen(const FnDescription *Desc, const CallEvent &Call,
471
472 void preFreopen(const FnDescription *Desc, const CallEvent &Call,
474 void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
476
477 void evalFclose(const FnDescription *Desc, const CallEvent &Call,
479
480 void preRead(const FnDescription *Desc, const CallEvent &Call,
482
483 void preWrite(const FnDescription *Desc, const CallEvent &Call,
485
486 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
488
489 void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
491
492 void evalFputx(const FnDescription *Desc, const CallEvent &Call,
494
495 void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
497
498 void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
500
501 void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
503
504 void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
506
507 void preFseek(const FnDescription *Desc, const CallEvent &Call,
509 void evalFseek(const FnDescription *Desc, const CallEvent &Call,
511
512 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
514
515 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
517
518 void evalFtell(const FnDescription *Desc, const CallEvent &Call,
520
521 void evalRewind(const FnDescription *Desc, const CallEvent &Call,
523
524 void preDefault(const FnDescription *Desc, const CallEvent &Call,
526
527 void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
529
530 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
532 const StreamErrorState &ErrorKind) const;
533
534 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
536 bool Indeterminate) const;
537
538 void preFflush(const FnDescription *Desc, const CallEvent &Call,
540
541 void evalFflush(const FnDescription *Desc, const CallEvent &Call,
543
544 void evalFileno(const FnDescription *Desc, const CallEvent &Call,
546
547
548
549
550
554
555
556
557
560
561
562
563
564
565
566
570
571
572
573
574
577
578
579
580
581
584
585
586
587
590
591
592
593 const FnDescription *lookupFn(const CallEvent &Call) const {
594
595
596 for (auto *P : Call.parameters()) {
599 T.getCanonicalType() != VaListType)
600 return nullptr;
601 }
602
604 }
605
606
607
609 const std::string &Message) const {
610 return C.getNoteTag([this, StreamSym,
613 return Message;
614 return "";
615 });
616 }
617
618 void initMacroValues(const Preprocessor &PP) const {
619 if (EofVal)
620 return;
621
623 EofVal = *OptInt;
624 else
625 EofVal = -1;
626 if (const std::optional OptInt = tryExpandAsInteger("SEEK_SET", PP))
627 SeekSetVal = *OptInt;
628 if (const std::optional OptInt = tryExpandAsInteger("SEEK_END", PP))
629 SeekEndVal = *OptInt;
630 if (const std::optional OptInt = tryExpandAsInteger("SEEK_CUR", PP))
631 SeekCurVal = *OptInt;
632 }
633
634
635
639};
640
641struct StreamOperationEvaluator {
644
646 const StreamState *SS = nullptr;
647 const CallExpr *CE = nullptr;
648 StreamErrorState NewES;
649
651 : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) {
652 ;
653 }
654
657 StreamSym = getStreamArg(Desc, Call).getAsSymbol();
658 if (!StreamSym)
659 return false;
660 SS = State->get(StreamSym);
661 if (!SS)
662 return false;
663 NewES = SS->ErrorState;
664 CE = dyn_cast_or_null(Call.getOriginExpr());
665 if (!CE)
666 return false;
667
668 assertStreamStateOpened(SS);
669
670 return true;
671 }
672
673 bool isStreamEof() const { return SS->ErrorState == ErrorFEof; }
674
677 }
678
680 const StreamState &NewSS) {
681 NewES = NewSS.ErrorState;
682 return State->set(StreamSym, NewSS);
683 }
684
687 return State->BindExpr(CE, C.getLocationContext(), RetVal);
688 }
689
691 uint64_t Val) {
692 return State->BindExpr(CE, C.getLocationContext(),
694 }
695
698 return State->BindExpr(CE, C.getLocationContext(), Val);
699 }
700
703 return State->BindExpr(CE, C.getLocationContext(),
704 C.getSValBuilder().makeNullWithType(CE->getType()));
705 }
706
712 if (!Cond)
713 return nullptr;
714 return State->assume(*Cond, true);
715 }
716
720 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
721 return C.getConstraintManager().assumeDual(State, RetVal);
722 }
723
725 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
726 bool SetFerror = NewES.FError && !SS->ErrorState.FError;
727 if (SetFeof && !SetFerror)
728 return Ch->constructSetEofNoteTag(C, StreamSym);
729 if (!SetFeof && SetFerror)
730 return Ch->constructSetErrorNoteTag(C, StreamSym);
731 if (SetFeof && SetFerror)
732 return Ch->constructSetEofOrErrorNoteTag(C, StreamSym);
733 return nullptr;
734 }
735};
736
737}
738
739
740
741
742
743namespace {
745protected:
746
747
748
749
750
751 bool isClosingCallAsWritten(const CallExpr &Call) const {
752 const auto *StreamChk = static_cast<const StreamChecker *>(&Checker);
753 return StreamChk->FCloseDesc.matchesAsWritten(Call);
754 }
755
758 const FunctionDecl *FD = dyn_cast(Callee);
759
760
761
762
763
764
765
766 if (!FD || !FD->hasBody())
767 return false;
769
770 auto Matches =
773 if (const auto *Call = Match.getNodeAs<CallExpr>("call"))
774 if (isClosingCallAsWritten(*Call))
775 return true;
776 }
777
778
779 return false;
780 }
781
784 return CallEnterState->get(Sym) !=
785 CallExitEndState->get(Sym);
786 }
787
791 N->getState()->getStateManager().getContext().getSourceManager());
792 return std::make_shared(
793 L, "Returning without closing stream object or storing it for later "
794 "release");
795 }
796
797public:
798 NoStreamStateChangeVisitor(SymbolRef Sym, const StreamChecker *Checker)
800};
801
802}
803
808
809
810 if (!State->get(StreamSym))
812
814 while (N) {
816 if (!State->get(StreamSym))
817 return Pred;
818 Pred = N;
820 }
821
822 return nullptr;
823}
824
826 SValBuilder &SVB = State->getStateManager().getSValBuilder();
827 if (const llvm::APSInt *Int = SVB.getKnownValue(State, V))
828 return Int->tryExtValue();
829 return std::nullopt;
830}
831
832
833
834
837 unsigned BlockCount, const SubRegion *Buffer,
838 QualType ElemType, int64_t StartIndex,
839 int64_t ElementCount) {
840 constexpr auto DoNotInvalidateSuperRegion =
841 RegionAndSymbolInvalidationTraits::InvalidationKinds::
842 TK_DoNotInvalidateSuperRegion;
843
845 const ASTContext &Ctx = State->getStateManager().getContext();
846 SValBuilder &SVB = State->getStateManager().getSValBuilder();
848
850 EscapingVals.reserve(ElementCount);
851
853 for (auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) {
855 const auto *Element =
856 RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx);
858 ITraits.setTrait(Element, DoNotInvalidateSuperRegion);
859 }
860 return State->invalidateRegions(
861 EscapingVals, Call.getOriginExpr(), BlockCount, LCtx,
862 false,
863 nullptr, &Call, &ITraits);
864}
865
869 auto GetArgSVal = [&Call](int Idx) { return Call.getArgSVal(Idx); };
870 auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal));
871 State = State->invalidateRegions(EscapingVals, Call.getOriginExpr(),
872 C.blockCount(), C.getLocationContext(),
873 false,
874 nullptr);
875 return State;
876}
877
878
879
880
881
882void StreamChecker::checkPreCall(const CallEvent &Call,
884 const FnDescription *Desc = lookupFn(Call);
885 if (!Desc || !Desc->PreFn)
886 return;
887
888 Desc->PreFn(this, Desc, Call, C);
889}
890
892 const FnDescription *Desc = lookupFn(Call);
893 if (!Desc && TestMode)
894 Desc = FnTestDescriptions.lookup(Call);
895 if (!Desc || !Desc->EvalFn)
896 return false;
897
898 Desc->EvalFn(this, Desc, Call, C);
899
900 return C.isDifferent();
901}
902
903ProgramStateRef StreamChecker::assumeNoAliasingWithStdStreams(
907 if (!Var)
908 return State;
909 const auto *LCtx = C.getLocationContext();
910 auto &StoreMgr = C.getStoreManager();
911 auto &SVB = C.getSValBuilder();
912 SVal VarValue = State->getSVal(StoreMgr.getLValueVar(Var, LCtx));
913 auto NoAliasState =
915 .castAs();
916 return State->assume(NoAliasState, true);
917 };
918
919 assert(State);
920 State = assumeRetNE(State, StdinDecl);
921 State = assumeRetNE(State, StdoutDecl);
922 State = assumeRetNE(State, StderrDecl);
923 assert(State);
924 return State;
925}
926
927void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
930 const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr());
931 if (!CE)
932 return;
933
936 assert(RetSym && "RetVal must be a symbol here.");
937
938 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
939
940
941
943 std::tie(StateNotNull, StateNull) =
944 C.getConstraintManager().assumeDual(State, RetVal);
945
946 StateNotNull =
947 StateNotNull->set(RetSym, StreamState::getOpened(Desc));
948 StateNull =
949 StateNull->set(RetSym, StreamState::getOpenFailed(Desc));
950
951 StateNotNull = assumeNoAliasingWithStdStreams(StateNotNull, RetVal, C);
952
953 C.addTransition(StateNotNull,
954 constructLeakNoteTag(C, RetSym, "Stream opened here"));
955 C.addTransition(StateNull);
956}
957
958void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
960
962 State = ensureStreamNonNull(getStreamArg(Desc, Call),
963 Call.getArgExpr(Desc->StreamArgNo), C, State);
964 if (!State)
965 return;
966
967 C.addTransition(State);
968}
969
970void StreamChecker::evalFreopen(const FnDescription *Desc,
974
975 auto *CE = dyn_cast_or_null(Call.getOriginExpr());
976 if (!CE)
977 return;
978
979 std::optional StreamVal =
981 if (!StreamVal)
982 return;
983
984 SymbolRef StreamSym = StreamVal->getAsSymbol();
985
986
987 if (!StreamSym)
988 return;
989
990
991 if (!State->get(StreamSym))
992 return;
993
994
995
996
997
999 State->BindExpr(CE, C.getLocationContext(), *StreamVal);
1000
1001
1003 State->BindExpr(CE, C.getLocationContext(),
1004 C.getSValBuilder().makeNullWithType(CE->getType()));
1005
1006 StateRetNotNull =
1007 StateRetNotNull->set(StreamSym, StreamState::getOpened(Desc));
1008 StateRetNull =
1009 StateRetNull->set(StreamSym, StreamState::getOpenFailed(Desc));
1010
1011 C.addTransition(StateRetNotNull,
1012 constructLeakNoteTag(C, StreamSym, "Stream reopened here"));
1013 C.addTransition(StateRetNull);
1014}
1015
1016void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
1019 StreamOperationEvaluator E(C);
1020 if (.Init(Desc, Call, C, State))
1021 return;
1022
1023
1024
1025
1026 State = E.setStreamState(State, StreamState::getClosed(Desc));
1027
1028
1029 C.addTransition(E.bindReturnValue(State, C, 0));
1030 C.addTransition(E.bindReturnValue(State, C, *EofVal));
1031}
1032
1033void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,
1036 SVal StreamVal = getStreamArg(Desc, Call);
1037 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1038 State);
1039 if (!State)
1040 return;
1041 State = ensureStreamOpened(StreamVal, C, State);
1042 if (!State)
1043 return;
1044 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
1045 if (!State)
1046 return;
1047
1049 if (Sym && State->get(Sym)) {
1050 const StreamState *SS = State->get(Sym);
1051 if (SS->ErrorState & ErrorFEof)
1052 reportFEofWarning(Sym, C, State);
1053 } else {
1054 C.addTransition(State);
1055 }
1056}
1057
1058void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,
1061 SVal StreamVal = getStreamArg(Desc, Call);
1062 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1063 State);
1064 if (!State)
1065 return;
1066 State = ensureStreamOpened(StreamVal, C, State);
1067 if (!State)
1068 return;
1069 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
1070 if (!State)
1071 return;
1072
1073 C.addTransition(State);
1074}
1075
1077 if (!R)
1078 return {};
1079 if (const auto *ER = dyn_cast(R))
1080 return ER->getElementType();
1081 if (const auto *TR = dyn_cast(R))
1082 return TR->getValueType();
1083 if (const auto *SR = dyn_cast(R))
1084 return SR->getPointeeStaticType();
1085 return {};
1086}
1087
1090 if (!R)
1091 return std::nullopt;
1092
1093 auto Zero = [&SVB] {
1096 };
1097
1098 if (const auto *ER = dyn_cast(R))
1099 return ER->getIndex();
1100 if (isa(R))
1101 return Zero();
1102 if (isa(R))
1103 return Zero();
1104 return std::nullopt;
1105}
1106
1111
1112 const auto *Buffer =
1113 dyn_cast_or_null(Call.getArgSVal(0).getAsRegion());
1114
1115 const ASTContext &Ctx = C.getASTContext();
1117 std::optional StartElementIndex =
1119
1120
1121 if (const auto *ER = dyn_cast_or_null(Buffer))
1122 Buffer = dyn_cast(ER->getSuperRegion());
1123
1124 std::optional<int64_t> CountVal = getKnownValue(State, NMembVal);
1125 std::optional<int64_t> Size = getKnownValue(State, SizeVal);
1126 std::optional<int64_t> StartIndexVal =
1128
1129 if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {
1130 int64_t NumBytesRead = Size.value() * CountVal.value();
1132 if (ElemSizeInChars == 0 || NumBytesRead < 0)
1133 return nullptr;
1134
1135 bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
1136 int64_t NumCompleteOrIncompleteElementsRead =
1137 NumBytesRead / ElemSizeInChars + IncompleteLastElement;
1138
1139 constexpr int MaxInvalidatedElementsLimit = 64;
1140 if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {
1142 ElemTy, *StartIndexVal,
1143 NumCompleteOrIncompleteElementsRead);
1144 }
1145 }
1146 return nullptr;
1147}
1148
1149void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
1151 bool IsFread) const {
1153 StreamOperationEvaluator E(C);
1154 if (.Init(Desc, Call, C, State))
1155 return;
1156
1157 std::optional SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
1158 if (!SizeVal)
1159 return;
1160 std::optional NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
1161 if (!NMembVal)
1162 return;
1163
1164
1165
1166
1167
1168
1169 if (State->isNull(*SizeVal).isConstrainedTrue() ||
1170 State->isNull(*NMembVal).isConstrainedTrue()) {
1171
1172
1173 C.addTransition(E.bindReturnValue(State, C, 0));
1174 return;
1175 }
1176
1177
1178
1179 if (IsFread && .isStreamEof()) {
1180
1181
1183 State, C, Call, *SizeVal, *NMembVal);
1184 State =
1185 InvalidatedState ? InvalidatedState : escapeArgs(State, C, Call, {0});
1186 }
1187
1188
1189
1190 if (!IsFread || .isStreamEof()) {
1192 State->BindExpr(E.CE, C.getLocationContext(), *NMembVal);
1193 StateNotFailed =
1194 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1195 C.addTransition(StateNotFailed);
1196 }
1197
1198
1199
1200 if (!IsFread && !PedanticMode)
1201 return;
1202
1205 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1206 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
1207 if (!StateFailed)
1208 return;
1209
1210 StreamErrorState NewES;
1211 if (IsFread)
1212 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1213 else
1214 NewES = ErrorFError;
1215
1216
1217 StateFailed = E.setStreamState(
1218 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1219 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1220}
1221
1222void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
1224
1225
1226
1228 StreamOperationEvaluator E(C);
1229 if (.Init(Desc, Call, C, State))
1230 return;
1231
1232 if (.isStreamEof()) {
1233
1234
1236 if (SingleChar) {
1237
1240 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1241
1242
1243 StateNotFailed = StateNotFailed->assumeInclusiveRange(
1244 RetVal,
1245 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1246 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1247 true);
1248 if (!StateNotFailed)
1249 return;
1250 C.addTransition(StateNotFailed);
1251 } else {
1252
1253 std::optional GetBuf =
1255 if (!GetBuf)
1256 return;
1258 State->BindExpr(E.CE, C.getLocationContext(), *GetBuf);
1259 StateNotFailed =
1260 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1261 C.addTransition(StateNotFailed);
1262 }
1263 }
1264
1265
1267 if (SingleChar)
1268 StateFailed = E.bindReturnValue(State, C, *EofVal);
1269 else
1270 StateFailed = E.bindNullReturnValue(State, C);
1271
1272
1273
1274 StreamErrorState NewES =
1275 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1276 StateFailed = E.setStreamState(
1277 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1278 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1279}
1280
1281void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
1283
1284
1285
1287 StreamOperationEvaluator E(C);
1288 if (.Init(Desc, Call, C, State))
1289 return;
1290
1291 if (IsSingleChar) {
1292
1293 std::optional PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1294 if (!PutVal)
1295 return;
1297 State->BindExpr(E.CE, C.getLocationContext(), *PutVal);
1298 StateNotFailed =
1299 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1300 C.addTransition(StateNotFailed);
1301 } else {
1302
1305 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1306 StateNotFailed =
1307 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1308 if (!StateNotFailed)
1309 return;
1310 StateNotFailed =
1311 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1312 C.addTransition(StateNotFailed);
1313 }
1314
1315 if (!PedanticMode)
1316 return;
1317
1318
1319
1320 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1321 StateFailed = E.setStreamState(
1322 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1323 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1324}
1325
1326void StreamChecker::evalFprintf(const FnDescription *Desc,
1329 if (Call.getNumArgs() < 2)
1330 return;
1331
1333 StreamOperationEvaluator E(C);
1334 if (.Init(Desc, Call, C, State))
1335 return;
1336
1338 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1339 auto Cond =
1340 E.SVB
1341 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1342 E.SVB.getConditionType())
1343 .getAs();
1344 if (!Cond)
1345 return;
1347 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1348
1349 StateNotFailed =
1350 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1351 C.addTransition(StateNotFailed);
1352
1353 if (!PedanticMode)
1354 return;
1355
1356
1357
1358 StateFailed = E.setStreamState(
1359 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1360 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1361}
1362
1363void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
1365 if (Call.getNumArgs() < 2)
1366 return;
1367
1369 StreamOperationEvaluator E(C);
1370 if (.Init(Desc, Call, C, State))
1371 return;
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381 if (.isStreamEof()) {
1384 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1385 StateNotFailed =
1386 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1387 if (!StateNotFailed)
1388 return;
1389
1390 if (auto const *Callee = Call.getCalleeIdentifier();
1391 || Callee->getName() != "vfscanf") {
1393 for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
1394 EscArgs.push_back(EscArg);
1395 StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
1396 }
1397
1398 if (StateNotFailed)
1399 C.addTransition(StateNotFailed);
1400 }
1401
1402
1403
1404
1405
1406
1407
1408 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1409 StreamErrorState NewES =
1410 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1411 StateFailed = E.setStreamState(
1412 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1413 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1414}
1415
1416void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,
1419 StreamOperationEvaluator E(C);
1420 if (.Init(Desc, Call, C, State))
1421 return;
1422
1423
1424 std::optional PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1425 if (!PutVal)
1426 return;
1427 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal);
1428 StateNotFailed =
1429 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1430 C.addTransition(StateNotFailed);
1431
1432
1433
1434
1435
1436
1437
1438 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1439 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1440 C.addTransition(StateFailed);
1441}
1442
1443void StreamChecker::evalGetdelim(const FnDescription *Desc,
1447 StreamOperationEvaluator E(C);
1448 if (.Init(Desc, Call, C, State))
1449 return;
1450
1451
1452
1453
1454
1455
1456
1457 if (.isStreamEof()) {
1458
1459
1461
1462
1464 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);
1465 StateNotFailed =
1466 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1467
1468
1470 if (NewLinePtr && isa(*NewLinePtr))
1471 StateNotFailed = StateNotFailed->assume(
1473
1474
1475
1476 SVal SizePtrSval = Call.getArgSVal(1);
1478 if (NVal && isa(*NVal)) {
1479 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1480 NVal->castAs<NonLoc>(), RetVal);
1481 StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);
1482 }
1483 if (!StateNotFailed)
1484 return;
1485 C.addTransition(StateNotFailed);
1486 }
1487
1488
1489
1490
1492 StreamErrorState NewES =
1493 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1494 StateFailed = E.setStreamState(
1495 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1496
1498 StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
1499 C.getLocationContext());
1500 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1501}
1502
1503void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
1506 SVal StreamVal = getStreamArg(Desc, Call);
1507 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1508 State);
1509 if (!State)
1510 return;
1511 State = ensureStreamOpened(StreamVal, C, State);
1512 if (!State)
1513 return;
1514 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
1515 if (!State)
1516 return;
1517
1518 C.addTransition(State);
1519}
1520
1521void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
1524 StreamOperationEvaluator E(C);
1525 if (.Init(Desc, Call, C, State))
1526 return;
1527
1528
1529 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0);
1530
1531 StateNotFailed =
1532 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1533 C.addTransition(StateNotFailed);
1534
1535 if (!PedanticMode)
1536 return;
1537
1538
1539
1540
1541
1542
1543
1545 StateFailed = E.setStreamState(
1546 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1547 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1548}
1549
1550void StreamChecker::evalFgetpos(const FnDescription *Desc,
1554 StreamOperationEvaluator E(C);
1555 if (.Init(Desc, Call, C, State))
1556 return;
1557
1559 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1560 StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1});
1561
1562
1563
1564
1565 C.addTransition(StateNotFailed);
1566 C.addTransition(StateFailed);
1567}
1568
1569void StreamChecker::evalFsetpos(const FnDescription *Desc,
1573 StreamOperationEvaluator E(C);
1574 if (.Init(Desc, Call, C, State))
1575 return;
1576
1578 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1579
1580 StateNotFailed = E.setStreamState(
1581 StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false));
1582 C.addTransition(StateNotFailed);
1583
1584 if (!PedanticMode)
1585 return;
1586
1587
1588
1589
1590
1591 StateFailed = E.setStreamState(
1592 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1593
1594 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1595}
1596
1597void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1600 StreamOperationEvaluator E(C);
1601 if (.Init(Desc, Call, C, State))
1602 return;
1603
1606 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1607 StateNotFailed =
1608 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1609 if (!StateNotFailed)
1610 return;
1611
1613
1614
1615
1616
1617 C.addTransition(StateNotFailed);
1618 C.addTransition(StateFailed);
1619}
1620
1621void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1624 StreamOperationEvaluator E(C);
1625 if (.Init(Desc, Call, C, State))
1626 return;
1627
1628 State =
1629 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false));
1630 C.addTransition(State);
1631}
1632
1633void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
1636 SVal StreamVal = getStreamArg(Desc, Call);
1637 std::optional Stream = StreamVal.getAs<DefinedSVal>();
1638 if (!Stream)
1639 return;
1640
1642 std::tie(StateNotNull, StateNull) =
1643 C.getConstraintManager().assumeDual(State, *Stream);
1644 if (StateNotNull && !StateNull)
1645 ensureStreamOpened(StreamVal, C, StateNotNull);
1646}
1647
1648void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1651 SVal StreamVal = getStreamArg(Desc, Call);
1652 std::optional Stream = StreamVal.getAs<DefinedSVal>();
1653 if (!Stream)
1654 return;
1655
1656
1658 std::tie(StateNotNull, StateNull) =
1659 C.getConstraintManager().assumeDual(State, *Stream);
1660 if (StateNotNull && StateNull)
1661 return;
1662 if (StateNotNull && !StateNull)
1663 State = StateNotNull;
1664 else
1665 State = StateNull;
1666
1667 const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr());
1668 if (!CE)
1669 return;
1670
1671
1672 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1674
1675
1676 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
1677 const StreamState *SS) {
1678 if (SS->ErrorState & ErrorFError) {
1679 StreamErrorState NewES =
1680 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1681 StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
1682 StateNotFailed = StateNotFailed->set(Sym, NewSS);
1683 }
1684 };
1685
1686 if (StateNotNull && !StateNull) {
1687
1689 const StreamState *SS = State->get(StreamSym);
1690 if (SS) {
1691 assert(SS->isOpened() && "Stream is expected to be opened");
1692 ClearErrorInNotFailed(StreamSym, SS);
1693 } else
1694 return;
1695 }
1696 } else {
1697
1698 const StreamMapTy &Map = StateNotFailed->get();
1699 for (const auto &I : Map) {
1701 const StreamState &SS = I.second;
1702 if (SS.isOpened())
1703 ClearErrorInNotFailed(Sym, &SS);
1704 }
1705 }
1706
1707 C.addTransition(StateNotFailed);
1708 C.addTransition(StateFailed);
1709}
1710
1711void StreamChecker::evalClearerr(const FnDescription *Desc,
1715 StreamOperationEvaluator E(C);
1716 if (.Init(Desc, Call, C, State))
1717 return;
1718
1719
1720 State = E.setStreamState(
1721 State,
1722 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1723 C.addTransition(State);
1724}
1725
1726void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1728 const StreamErrorState &ErrorKind) const {
1730 StreamOperationEvaluator E(C);
1731 if (.Init(Desc, Call, C, State))
1732 return;
1733
1734 if (E.SS->ErrorState & ErrorKind) {
1735
1736
1737
1739 C.addTransition(E.setStreamState(
1740 TrueState, StreamState::getOpened(Desc, ErrorKind,
1741 E.SS->FilePositionIndeterminate &&
1742 !ErrorKind.isFEof())));
1743 }
1744 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1745
1746
1747
1749 C.addTransition(E.setStreamState(
1750 FalseState,
1751 StreamState::getOpened(
1752 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1753 }
1754}
1755
1756void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call,
1758
1759
1760
1761
1762
1763
1764
1765
1766
1768 StreamOperationEvaluator E(C);
1769 if (.Init(Desc, Call, C, State))
1770 return;
1771
1773 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1774 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call));
1775 if (!State)
1776 return;
1777
1778 C.addTransition(State);
1779}
1780
1781void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1784 SVal StreamVal = getStreamArg(Desc, Call);
1785 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1786 State);
1787 if (!State)
1788 return;
1789 State = ensureStreamOpened(StreamVal, C, State);
1790 if (!State)
1791 return;
1792
1793 C.addTransition(State);
1794}
1795
1796void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1798 const StreamErrorState &ErrorKind,
1799 bool Indeterminate) const {
1801 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1802 assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1803 const StreamState *SS = State->get(StreamSym);
1804 assert(SS && "Stream should be tracked by the checker.");
1805 State = State->set(
1806 StreamSym,
1807 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1808 C.addTransition(State);
1809}
1810
1812StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1816 if (!Stream)
1817 return State;
1818
1820
1822 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1823
1824 if (!StateNotNull && StateNull) {
1825 if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1826 auto R = std::make_unique(
1827 BT_FileNull, "Stream pointer might be NULL.", N);
1828 if (StreamE)
1830 C.emitReport(std::move(R));
1831 }
1832 return nullptr;
1833 }
1834
1835 return StateNotNull;
1836}
1837
1838namespace {
1841 bool Satisfied = false;
1842
1843public:
1844 explicit StreamClosedVisitor(SymbolRef StreamSym) : StreamSym(StreamSym) {}
1845
1846 static void *getTag() {
1847 static int Tag = 0;
1848 return &Tag;
1849 }
1850
1851 void Profile(llvm::FoldingSetNodeID &ID) const override {
1852 ID.AddPointer(getTag());
1853 ID.AddPointer(StreamSym);
1854 }
1855
1859 if (Satisfied)
1860 return nullptr;
1861 const StreamState *PredSS =
1863 if (PredSS && PredSS->isClosed())
1864 return nullptr;
1865
1867 if (!S)
1868 return nullptr;
1869 Satisfied = true;
1872 llvm::StringLiteral Msg = "Stream is closed here";
1873 return std::make_shared(Pos, Msg);
1874 }
1875};
1876}
1877
1882 if (!Sym)
1883 return State;
1884
1885 const StreamState *SS = State->get(Sym);
1886 if (!SS)
1887 return State;
1888
1889 if (SS->isClosed()) {
1890
1891
1893 auto R = std::make_unique(
1894 BT_UseAfterClose, "Use of a stream that might be already closed", N);
1895 R->addVisitor(Sym);
1896 C.emitReport(std::move(R));
1897 return nullptr;
1898 }
1899
1900 return State;
1901 }
1902
1903 if (SS->isOpenFailed()) {
1904
1905
1906
1907
1909 if (N) {
1910 C.emitReport(std::make_unique(
1911 BT_UseAfterOpenFailed,
1912 "Stream might be invalid after "
1913 "(re-)opening it has failed. "
1914 "Can cause undefined behaviour.",
1915 N));
1916 return nullptr;
1917 }
1918 }
1919
1920 return State;
1921}
1922
1923ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1925 static const char *BugMessage =
1926 "File position of the stream might be 'indeterminate' "
1927 "after a failed operation. "
1928 "Can cause undefined behavior.";
1929
1931 if (!Sym)
1932 return State;
1933
1934 const StreamState *SS = State->get(Sym);
1935 if (!SS)
1936 return State;
1937
1938 assert(SS->isOpened() && "First ensure that stream is opened.");
1939
1940 if (SS->FilePositionIndeterminate) {
1941 if (SS->ErrorState & ErrorFEof) {
1942
1943
1944
1945 ExplodedNode *N = C.generateNonFatalErrorNode(State);
1946 if (!N)
1947 return nullptr;
1948
1949 auto R = std::make_unique(
1950 BT_IndeterminatePosition, BugMessage, N);
1951 R->markInteresting(Sym);
1952 C.emitReport(std::move(R));
1953 return State->set(
1954 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1955 }
1956
1957
1958
1959 if (ExplodedNode *N = C.generateErrorNode(State)) {
1960 auto R = std::make_unique(
1961 BT_IndeterminatePosition, BugMessage, N);
1962 R->markInteresting(Sym);
1963 C.emitReport(std::move(R));
1964 }
1965
1966 return nullptr;
1967 }
1968
1969 return State;
1970}
1971
1973StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1975 std::optionalnonloc::ConcreteInt CI =
1977 if (!CI)
1978 return State;
1979
1980 int64_t X = CI->getValue()->getSExtValue();
1981 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1982 return State;
1983
1984 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1985 C.emitReport(std::make_unique(
1986 BT_IllegalWhence,
1987 "The whence argument to fseek() should be "
1988 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1989 N));
1990 return nullptr;
1991 }
1992
1993 return State;
1994}
1995
1998 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1999 auto R = std::make_unique(
2000 BT_StreamEof,
2001 "Read function called when stream is in EOF state. "
2002 "Function has no effect.",
2003 N);
2004 R->markInteresting(StreamSym);
2005 C.emitReport(std::move(R));
2006 return;
2007 }
2008 C.addTransition(State);
2009}
2010
2014 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
2015 if (!Err)
2016 return Pred;
2017
2018 for (SymbolRef LeakSym : LeakedSyms) {
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
2031 assert(StreamOpenNode && "Could not find place of stream opening.");
2032
2036 StreamStmt, C.getSourceManager(),
2038
2039 std::unique_ptr R =
2040 std::make_unique(
2041 BT_ResourceLeak,
2042 "Opened stream never closed. Potential resource leak.", Err,
2043 LocUsedForUniqueing,
2045 R->markInteresting(LeakSym);
2046 R->addVisitor(LeakSym, this);
2047 C.emitReport(std::move(R));
2048 }
2049
2050 return Err;
2051}
2052
2053void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
2056
2058
2059 const StreamMapTy &Map = State->get();
2060 for (const auto &I : Map) {
2062 const StreamState &SS = I.second;
2063 if (!SymReaper.isDead(Sym))
2064 continue;
2065 if (SS.isOpened())
2066 LeakedSyms.push_back(Sym);
2067 State = State->remove(Sym);
2068 }
2069
2071 if (!LeakedSyms.empty())
2072 N = reportLeaks(LeakedSyms, C, N);
2073
2074 C.addTransition(State, N);
2075}
2076
2080
2081
2082
2084 return State;
2085
2087
2088
2089
2090
2091
2092
2093
2094 State = State->remove(Sym);
2095 }
2096 return State;
2097}
2098
2104
2105 if (FileTy.isNull())
2106 return nullptr;
2107
2109
2111 for (const Decl *D : LookupRes) {
2112 if (auto *VD = dyn_cast_or_null(D)) {
2113 if (SM.isInSystemHeader(VD->getLocation()) && VD->hasExternalStorage() &&
2114 VD->getType().getCanonicalType() == FilePtrTy) {
2115 return VD;
2116 }
2117 }
2118 }
2119 return nullptr;
2120}
2121
2129}
2130
2131
2132
2133
2134
2135void ento::registerStreamChecker(CheckerManager &Mgr) {
2139}
2140
2141bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
2142 return true;
2143}
2144
2145void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
2147 Checker->TestMode = true;
2148}
2149
2150bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
2151 return true;
2152}
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static const VarDecl * getGlobalStreamPointerByName(const TranslationUnitDecl *TU, StringRef VarName)
static ProgramStateRef tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, NonLoc SizeVal, NonLoc NMembVal)
static QualType getPointeeType(const MemRegion *R)
static std::optional< int64_t > getKnownValue(ProgramStateRef State, SVal V)
static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, ArrayRef< unsigned int > EscapingArgs)
static std::optional< NonLoc > getStartIndex(SValBuilder &SVB, const MemRegion *R)
static ProgramStateRef escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call, unsigned BlockCount, const SubRegion *Buffer, QualType ElemType, int64_t StartIndex, int64_t ElementCount)
Invalidate only the requested elements instead of the whole buffer.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
QualType getBuiltinVaListType() const
Retrieve the type of the __builtin_va_list type.
QualType getFILEType() const
Retrieve the C FILE type.
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
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]).
QualType getCallReturnType(const ASTContext &Ctx) const
getCallReturnType - Get the return type of the call expr.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Decl - This represents one declaration (or definition), e.g.
This represents one expression.
Represents a function declaration or definition.
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
A (possibly-)qualified type.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
QualType getCanonicalType() const
Stmt - This represents one statement.
The top declaration context.
ASTContext & getASTContext() const
bool isPointerType() const
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Represents a variable declaration or definition.
Maps string IDs to AST nodes matched by parts of a matcher.
Preprocessor & getPreprocessor() override
APSIntPtr getIntValue(uint64_t X, bool isUnsigned)
const BugType & getBugType() const
const SourceManager & getSourceManager() const
BugReporterVisitors are used to add custom diagnostics along a path.
virtual void Profile(llvm::FoldingSetNodeID &ID) const =0
virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, PathSensitiveBugReport &BR)=0
Return a diagnostic piece which should be associated with the given node.
BugReporter is a utility class for generating PathDiagnostics for analysis.
An immutable map from CallDescriptions to arbitrary data.
const T * lookup(const CallEvent &Call) const
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
Represents an abstract call to a function or method along a particular path.
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
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
virtual bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx)=0
Heuristically guess whether the callee intended to free the resource.
const CheckerBase & Checker
virtual PathDiagnosticPieceRef emitNote(const ExplodedNode *N)=0
virtual bool hasResourceStateChanged(ProgramStateRef CallEnterState, ProgramStateRef CallExitEndState)=0
The tag upon which the TagVisitor reacts.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
void markNotInteresting(SymbolRef sym)
bool isInteresting(SymbolRef sym) const
Information about invalidation for a particular region/symbol.
void setTrait(SymbolRef Sym, InvalidationKinds IK)
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
virtual const llvm::APSInt * getKnownValue(ProgramStateRef state, SVal val)=0
Evaluates a given SVal.
BasicValueFactory & getBasicValueFactory()
NonLoc makeArrayIndex(uint64_t idx)
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
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
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
SubRegion - A region that subsets another larger region.
MemRegionManager & getMemRegionManager() const override
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.
__inline void unsigned int _2
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
internal::Matcher< T > findAll(const internal::Matcher< T > &Matcher)
Matches if the node or any descendant matches.
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.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
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.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
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