24#include "llvm/ADT/Sequence.h"
30using namespace std::placeholders;
46struct StreamErrorState {
54 bool isNoError()
const {
return NoError && !FEof && !FError; }
55 bool isFEof()
const {
return !NoError && FEof && !FError; }
56 bool isFError()
const {
return !NoError && !FEof && FError; }
58 bool operator==(
const StreamErrorState &ES)
const {
59 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
62 bool operator!=(
const StreamErrorState &ES)
const {
return !(*
this == ES); }
64 StreamErrorState
operator|(
const StreamErrorState &E)
const {
65 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
68 StreamErrorState
operator&(
const StreamErrorState &E)
const {
69 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
72 StreamErrorState
operator~()
const {
return {!NoError, !FEof, !FError}; }
75 operator bool()
const {
return NoError || FEof || FError; }
77 void Profile(llvm::FoldingSetNodeID &ID)
const {
78 ID.AddBoolean(NoError);
80 ID.AddBoolean(FError);
84const StreamErrorState ErrorNone{
true,
false,
false};
85const StreamErrorState ErrorFEof{
false,
true,
false};
86const StreamErrorState ErrorFError{
false,
false,
true};
92 const FnDescription *LastOperation;
103 StreamErrorState
const ErrorState;
113 bool const FilePositionIndeterminate =
false;
115 StreamState(
const FnDescription *L, KindTy S,
const StreamErrorState &ES,
116 bool IsFilePositionIndeterminate)
117 : LastOperation(L), State(S), ErrorState(ES),
118 FilePositionIndeterminate(IsFilePositionIndeterminate) {
119 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
120 "FilePositionIndeterminate should be false in FEof case.");
121 assert((State == Opened || ErrorState.isNoError()) &&
122 "ErrorState should be None in non-opened stream state.");
125 bool isOpened()
const {
return State == Opened; }
126 bool isClosed()
const {
return State ==
Closed; }
127 bool isOpenFailed()
const {
return State == OpenFailed; }
132 return LastOperation ==
X.LastOperation && State ==
X.State &&
133 ErrorState ==
X.ErrorState &&
134 FilePositionIndeterminate ==
X.FilePositionIndeterminate;
137 static StreamState getOpened(
const FnDescription *L,
138 const StreamErrorState &ES = ErrorNone,
139 bool IsFilePositionIndeterminate =
false) {
140 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
142 static StreamState getClosed(
const FnDescription *L) {
143 return StreamState{L,
Closed, {},
false};
145 static StreamState getOpenFailed(
const FnDescription *L) {
146 return StreamState{L, OpenFailed, {},
false};
149 void Profile(llvm::FoldingSetNodeID &ID)
const {
150 ID.AddPointer(LastOperation);
151 ID.AddInteger(State);
152 ErrorState.Profile(ID);
153 ID.AddBoolean(FilePositionIndeterminate);
171using FnCheck = std::function<void(
const StreamChecker *,
const FnDescription *,
174using ArgNoTy =
unsigned int;
175static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
177const char *FeofNote =
"Assuming stream reaches end-of-file here";
178const char *FerrorNote =
"Assuming this stream operation fails";
180struct FnDescription {
189 assert(Desc && Desc->StreamArgNo != ArgNone &&
190 "Try to get a non-existing stream argument.");
191 return Call.getArgSVal(Desc->StreamArgNo);
196 assert(CE &&
"Expecting a call expression.");
199 return C.getSValBuilder()
200 .conjureSymbolVal(
nullptr, CE, LCtx,
C.blockCount())
207 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
208 State = State->assume(RetVal,
true);
209 assert(State &&
"Assumption on new value should not fail.");
215 State = State->BindExpr(CE,
C.getLocationContext(),
220inline void assertStreamStateOpened(
const StreamState *SS) {
221 assert(SS->isOpened() &&
"Stream is expected to be opened");
224class StreamChecker :
public Checker<check::PreCall, eval::Call,
225 check::DeadSymbols, check::PointerEscape> {
226 BugType BT_FileNull{
this,
"NULL stream pointer",
"Stream handling error"};
227 BugType BT_UseAfterClose{
this,
"Closed stream",
"Stream handling error"};
228 BugType BT_UseAfterOpenFailed{
this,
"Invalid stream",
229 "Stream handling error"};
230 BugType BT_IndeterminatePosition{
this,
"Invalid stream state",
231 "Stream handling error"};
232 BugType BT_IllegalWhence{
this,
"Illegal whence argument",
233 "Stream handling error"};
234 BugType BT_StreamEof{
this,
"Stream already in EOF",
"Stream handling error"};
235 BugType BT_ResourceLeak{
this,
"Resource leak",
"Stream handling error",
247 const BugType *getBT_StreamEof()
const {
return &BT_StreamEof; }
248 const BugType *getBT_IndeterminatePosition()
const {
249 return &BT_IndeterminatePosition;
269 &BR.
getBugType() != this->getBT_IndeterminatePosition())
285 BR.markNotInteresting(StreamSym);
288 if (&BR.
getBugType() == this->getBT_IndeterminatePosition()) {
289 BR.markNotInteresting(StreamSym);
298 bool TestMode =
false;
301 bool PedanticMode =
false;
305 {{CDM::CLibrary, {
"fopen"}, 2},
306 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
307 {{CDM::CLibrary, {
"fdopen"}, 2},
308 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
309 {{CDM::CLibrary, {
"freopen"}, 3},
310 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
311 {{CDM::CLibrary, {
"tmpfile"}, 0},
312 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
313 {{CDM::CLibrary, {
"fclose"}, 1},
314 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
315 {{CDM::CLibrary, {
"fread"}, 4},
316 {&StreamChecker::preRead,
317 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
true), 3}},
318 {{CDM::CLibrary, {
"fwrite"}, 4},
319 {&StreamChecker::preWrite,
320 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
false), 3}},
321 {{CDM::CLibrary, {
"fgetc"}, 1},
322 {&StreamChecker::preRead,
323 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
324 {{CDM::CLibrary, {
"fgets"}, 3},
325 {&StreamChecker::preRead,
326 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
false), 2}},
327 {{CDM::CLibrary, {
"getc"}, 1},
328 {&StreamChecker::preRead,
329 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
330 {{CDM::CLibrary, {
"fputc"}, 2},
331 {&StreamChecker::preWrite,
332 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
333 {{CDM::CLibrary, {
"fputs"}, 2},
334 {&StreamChecker::preWrite,
335 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
false), 1}},
336 {{CDM::CLibrary, {
"putc"}, 2},
337 {&StreamChecker::preWrite,
338 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
339 {{CDM::CLibrary, {
"fprintf"}},
340 {&StreamChecker::preWrite,
341 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
342 {{CDM::CLibrary, {
"vfprintf"}, 3},
343 {&StreamChecker::preWrite,
344 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
345 {{CDM::CLibrary, {
"fscanf"}},
346 {&StreamChecker::preRead,
347 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
348 {{CDM::CLibrary, {
"vfscanf"}, 3},
349 {&StreamChecker::preRead,
350 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
351 {{CDM::CLibrary, {
"ungetc"}, 2},
352 {&StreamChecker::preWrite,
353 std::bind(&StreamChecker::evalUngetc, _1,
_2, _3, _4), 1}},
354 {{CDM::CLibrary, {
"getdelim"}, 4},
355 {&StreamChecker::preRead,
356 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 3}},
357 {{CDM::CLibrary, {
"getline"}, 3},
358 {&StreamChecker::preRead,
359 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 2}},
360 {{CDM::CLibrary, {
"fseek"}, 3},
361 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
362 {{CDM::CLibrary, {
"fseeko"}, 3},
363 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
364 {{CDM::CLibrary, {
"ftell"}, 1},
365 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
366 {{CDM::CLibrary, {
"ftello"}, 1},
367 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
368 {{CDM::CLibrary, {
"fflush"}, 1},
369 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
370 {{CDM::CLibrary, {
"rewind"}, 1},
371 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
372 {{CDM::CLibrary, {
"fgetpos"}, 2},
373 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
374 {{CDM::CLibrary, {
"fsetpos"}, 2},
375 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
376 {{CDM::CLibrary, {
"clearerr"}, 1},
377 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
378 {{CDM::CLibrary, {
"feof"}, 1},
379 {&StreamChecker::preDefault,
380 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFEof),
382 {{CDM::CLibrary, {
"ferror"}, 1},
383 {&StreamChecker::preDefault,
384 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFError),
386 {{CDM::CLibrary, {
"fileno"}, 1},
387 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
391 {{CDM::SimpleFunc, {
"StreamTesterChecker_make_feof_stream"}, 1},
393 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4, ErrorFEof,
396 {{CDM::SimpleFunc, {
"StreamTesterChecker_make_ferror_stream"}, 1},
398 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
402 {
"StreamTesterChecker_make_ferror_indeterminate_stream"},
405 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
411 mutable std::optional<int> EofVal;
413 mutable int SeekSetVal = 0;
415 mutable int SeekCurVal = 1;
417 mutable int SeekEndVal = 2;
421 void evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
424 void preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
426 void evalFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
429 void evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
432 void preRead(
const FnDescription *Desc,
const CallEvent &
Call,
435 void preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
438 void evalFreadFwrite(
const FnDescription *Desc,
const CallEvent &
Call,
441 void evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
444 void evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
447 void evalFprintf(
const FnDescription *Desc,
const CallEvent &
Call,
450 void evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
453 void evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
456 void evalGetdelim(
const FnDescription *Desc,
const CallEvent &
Call,
459 void preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
461 void evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
464 void evalFgetpos(
const FnDescription *Desc,
const CallEvent &
Call,
467 void evalFsetpos(
const FnDescription *Desc,
const CallEvent &
Call,
470 void evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
473 void evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
476 void preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
479 void evalClearerr(
const FnDescription *Desc,
const CallEvent &
Call,
482 void evalFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
484 const StreamErrorState &ErrorKind)
const;
486 void evalSetFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
488 bool Indeterminate)
const;
490 void preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
493 void evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
496 void evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
548 for (
auto *
P :
Call.parameters()) {
551 T.getCanonicalType() != VaListType)
561 const std::string &Message)
const {
562 return C.getNoteTag([
this, StreamSym,
574 if (
const std::optional<int> OptInt =
579 if (
const std::optional<int> OptInt =
581 SeekSetVal = *OptInt;
582 if (
const std::optional<int> OptInt =
584 SeekEndVal = *OptInt;
585 if (
const std::optional<int> OptInt =
587 SeekCurVal = *OptInt;
591 VaListType =
C.getASTContext().getBuiltinVaListType().getCanonicalType();
601struct StreamOperationEvaluator {
606 const StreamState *SS =
nullptr;
608 StreamErrorState NewES;
611 : SVB(
C.getSValBuilder()), ACtx(
C.getASTContext()) {
617 StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
620 SS = State->get<StreamMap>(StreamSym);
623 NewES = SS->ErrorState;
624 CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
628 assertStreamStateOpened(SS);
633 bool isStreamEof()
const {
return SS->ErrorState == ErrorFEof; }
640 const StreamState &NewSS) {
641 NewES = NewSS.ErrorState;
642 return State->set<StreamMap>(StreamSym, NewSS);
647 return State->BindExpr(CE,
C.getLocationContext(), RetVal);
652 return State->BindExpr(CE,
C.getLocationContext(),
658 return State->BindExpr(CE,
C.getLocationContext(), Val);
663 return State->BindExpr(CE,
C.getLocationContext(),
664 C.getSValBuilder().makeNullWithType(CE->
getType()));
674 return State->assume(*Cond,
true);
680 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
681 return C.getConstraintManager().assumeDual(State, RetVal);
685 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
686 bool SetFerror = NewES.FError && !SS->ErrorState.FError;
687 if (SetFeof && !SetFerror)
688 return Ch->constructSetEofNoteTag(
C, StreamSym);
689 if (!SetFeof && SetFerror)
690 return Ch->constructSetErrorNoteTag(
C, StreamSym);
691 if (SetFeof && SetFerror)
692 return Ch->constructSetEofOrErrorNoteTag(
C, StreamSym);
705 if (!State->get<StreamMap>(StreamSym))
711 if (!State->get<StreamMap>(StreamSym))
723 const auto *CE =
Call.getOriginExpr();
726 EscapingVals.reserve(EscapingArgs.size());
727 for (
auto EscArgIdx : EscapingArgs)
728 EscapingVals.push_back(
Call.getArgSVal(EscArgIdx));
729 State = State->invalidateRegions(EscapingVals, CE,
C.blockCount(),
730 C.getLocationContext(),
744 const FnDescription *Desc = lookupFn(
Call);
745 if (!Desc || !Desc->PreFn)
748 Desc->PreFn(
this, Desc,
Call,
C);
752 const FnDescription *Desc = lookupFn(
Call);
753 if (!Desc && TestMode)
755 if (!Desc || !Desc->EvalFn)
758 Desc->EvalFn(
this, Desc,
Call,
C);
760 return C.isDifferent();
763void StreamChecker::evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
766 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
772 assert(RetSym &&
"RetVal must be a symbol here.");
774 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
779 std::tie(StateNotNull, StateNull) =
780 C.getConstraintManager().assumeDual(State, RetVal);
783 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
785 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
787 C.addTransition(StateNotNull,
788 constructLeakNoteTag(
C, RetSym,
"Stream opened here"));
789 C.addTransition(StateNull);
792void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
796 State = ensureStreamNonNull(getStreamArg(Desc,
Call),
797 Call.getArgExpr(Desc->StreamArgNo),
C, State);
801 C.addTransition(State);
804void StreamChecker::evalFreopen(
const FnDescription *Desc,
809 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
813 std::optional<DefinedSVal> StreamVal =
818 SymbolRef StreamSym = StreamVal->getAsSymbol();
825 if (!State->get<StreamMap>(StreamSym))
833 State->BindExpr(CE,
C.getLocationContext(), *StreamVal);
837 State->BindExpr(CE,
C.getLocationContext(),
838 C.getSValBuilder().makeNullWithType(CE->
getType()));
841 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
843 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
845 C.addTransition(StateRetNotNull,
846 constructLeakNoteTag(
C, StreamSym,
"Stream reopened here"));
847 C.addTransition(StateRetNull);
850void StreamChecker::evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
853 StreamOperationEvaluator E(
C);
854 if (!E.Init(Desc,
Call,
C, State))
860 State = E.setStreamState(State, StreamState::getClosed(Desc));
863 C.addTransition(E.bindReturnValue(State,
C, 0));
864 C.addTransition(E.bindReturnValue(State,
C, *EofVal));
867void StreamChecker::preRead(
const FnDescription *Desc,
const CallEvent &
Call,
870 SVal StreamVal = getStreamArg(Desc,
Call);
871 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
875 State = ensureStreamOpened(StreamVal,
C, State);
878 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
883 if (Sym && State->get<StreamMap>(Sym)) {
884 const StreamState *SS = State->get<StreamMap>(Sym);
885 if (SS->ErrorState & ErrorFEof)
886 reportFEofWarning(Sym,
C, State);
888 C.addTransition(State);
892void StreamChecker::preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
895 SVal StreamVal = getStreamArg(Desc,
Call);
896 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
900 State = ensureStreamOpened(StreamVal,
C, State);
903 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
907 C.addTransition(State);
910void StreamChecker::evalFreadFwrite(
const FnDescription *Desc,
912 bool IsFread)
const {
914 StreamOperationEvaluator E(
C);
915 if (!E.Init(Desc,
Call,
C, State))
918 std::optional<NonLoc> SizeVal =
Call.getArgSVal(1).getAs<
NonLoc>();
921 std::optional<NonLoc> NMembVal =
Call.getArgSVal(2).getAs<
NonLoc>();
930 if (State->isNull(*SizeVal).isConstrainedTrue() ||
931 State->isNull(*NMembVal).isConstrainedTrue()) {
934 C.addTransition(E.bindReturnValue(State,
C, 0));
940 if (IsFread && !E.isStreamEof())
945 if (!IsFread || !E.isStreamEof()) {
947 State->BindExpr(E.CE,
C.getLocationContext(), *NMembVal);
949 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
950 C.addTransition(StateNotFailed);
955 if (!IsFread && !PedanticMode)
960 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
961 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
965 StreamErrorState NewES;
967 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
972 StateFailed = E.setStreamState(
973 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
974 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
977void StreamChecker::evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
983 StreamOperationEvaluator E(
C);
984 if (!E.Init(Desc,
Call,
C, State))
987 if (!E.isStreamEof()) {
995 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
998 StateNotFailed = StateNotFailed->assumeInclusiveRange(
1000 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1001 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1003 if (!StateNotFailed)
1005 C.addTransition(StateNotFailed);
1008 std::optional<DefinedSVal> GetBuf =
1013 State->BindExpr(E.CE,
C.getLocationContext(), *GetBuf);
1015 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1016 C.addTransition(StateNotFailed);
1023 StateFailed = E.bindReturnValue(State,
C, *EofVal);
1025 StateFailed = E.bindNullReturnValue(State,
C);
1029 StreamErrorState NewES =
1030 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1031 StateFailed = E.setStreamState(
1032 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1033 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1036void StreamChecker::evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
1042 StreamOperationEvaluator E(
C);
1043 if (!E.Init(Desc,
Call,
C, State))
1048 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<
NonLoc>();
1052 State->BindExpr(E.CE,
C.getLocationContext(), *PutVal);
1054 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1055 C.addTransition(StateNotFailed);
1060 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1062 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1063 if (!StateNotFailed)
1066 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1067 C.addTransition(StateNotFailed);
1076 StateFailed = E.setStreamState(
1077 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1078 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1081void StreamChecker::evalFprintf(
const FnDescription *Desc,
1084 if (
Call.getNumArgs() < 2)
1088 StreamOperationEvaluator E(
C);
1089 if (!E.Init(Desc,
Call,
C, State))
1093 State = State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1096 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1097 E.SVB.getConditionType())
1098 .getAs<DefinedOrUnknownSVal>();
1102 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1105 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1106 C.addTransition(StateNotFailed);
1113 StateFailed = E.setStreamState(
1114 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1115 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1118void StreamChecker::evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
1120 if (
Call.getNumArgs() < 2)
1124 StreamOperationEvaluator E(
C);
1125 if (!E.Init(Desc,
Call,
C, State))
1136 if (!E.isStreamEof()) {
1139 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1141 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1142 if (!StateNotFailed)
1145 if (
auto const *Callee =
Call.getCalleeIdentifier();
1148 for (
auto EscArg : llvm::seq(2u,
Call.getNumArgs()))
1149 EscArgs.push_back(EscArg);
1154 C.addTransition(StateNotFailed);
1164 StreamErrorState NewES =
1165 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1166 StateFailed = E.setStreamState(
1167 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1168 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1171void StreamChecker::evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
1174 StreamOperationEvaluator E(
C);
1175 if (!E.Init(Desc,
Call,
C, State))
1179 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<
NonLoc>();
1184 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1185 C.addTransition(StateNotFailed);
1194 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1195 C.addTransition(StateFailed);
1198void StreamChecker::evalGetdelim(
const FnDescription *Desc,
1202 StreamOperationEvaluator E(
C);
1203 if (!E.Init(Desc,
Call,
C, State))
1212 if (!E.isStreamEof()) {
1221 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1225 if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
1226 StateNotFailed = StateNotFailed->assume(
1231 SVal SizePtrSval =
Call.getArgSVal(1);
1233 if (NVal && isa<NonLoc>(*NVal)) {
1234 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1235 NVal->castAs<
NonLoc>(), RetVal);
1236 StateNotFailed = E.bindReturnValue(StateNotFailed,
C, RetVal);
1238 if (!StateNotFailed)
1240 C.addTransition(StateNotFailed);
1247 StreamErrorState NewES =
1248 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1249 StateFailed = E.setStreamState(
1250 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1253 StateFailed = StateFailed->bindLoc(*NewLinePtr,
UndefinedVal(),
1254 C.getLocationContext());
1255 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1258void StreamChecker::preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1261 SVal StreamVal = getStreamArg(Desc,
Call);
1262 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1266 State = ensureStreamOpened(StreamVal,
C, State);
1269 State = ensureFseekWhenceCorrect(
Call.getArgSVal(2),
C, State);
1273 C.addTransition(State);
1276void StreamChecker::evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1279 StreamOperationEvaluator E(
C);
1280 if (!E.Init(Desc,
Call,
C, State))
1287 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1288 C.addTransition(StateNotFailed);
1300 StateFailed = E.setStreamState(
1301 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1302 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1305void StreamChecker::evalFgetpos(
const FnDescription *Desc,
1309 StreamOperationEvaluator E(
C);
1310 if (!E.Init(Desc,
Call,
C, State))
1314 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1320 C.addTransition(StateNotFailed);
1321 C.addTransition(StateFailed);
1324void StreamChecker::evalFsetpos(
const FnDescription *Desc,
1328 StreamOperationEvaluator E(
C);
1329 if (!E.Init(Desc,
Call,
C, State))
1333 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1335 StateNotFailed = E.setStreamState(
1336 StateNotFailed, StreamState::getOpened(Desc, ErrorNone,
false));
1337 C.addTransition(StateNotFailed);
1346 StateFailed = E.setStreamState(
1347 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1349 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1352void StreamChecker::evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
1355 StreamOperationEvaluator E(
C);
1356 if (!E.Init(Desc,
Call,
C, State))
1361 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1363 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1364 if (!StateNotFailed)
1372 C.addTransition(StateNotFailed);
1373 C.addTransition(StateFailed);
1376void StreamChecker::evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
1379 StreamOperationEvaluator E(
C);
1380 if (!E.Init(Desc,
Call,
C, State))
1384 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone,
false));
1385 C.addTransition(State);
1388void StreamChecker::preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1391 SVal StreamVal = getStreamArg(Desc,
Call);
1397 std::tie(StateNotNull, StateNull) =
1398 C.getConstraintManager().assumeDual(State, *Stream);
1399 if (StateNotNull && !StateNull)
1400 ensureStreamOpened(StreamVal,
C, StateNotNull);
1403void StreamChecker::evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1406 SVal StreamVal = getStreamArg(Desc,
Call);
1413 std::tie(StateNotNull, StateNull) =
1414 C.getConstraintManager().assumeDual(State, *Stream);
1415 if (StateNotNull && StateNull)
1417 if (StateNotNull && !StateNull)
1418 State = StateNotNull;
1422 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1431 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](
SymbolRef Sym,
1432 const StreamState *SS) {
1433 if (SS->ErrorState & ErrorFError) {
1434 StreamErrorState NewES =
1435 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1436 StreamState NewSS = StreamState::getOpened(Desc, NewES,
false);
1437 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1441 if (StateNotNull && !StateNull) {
1444 const StreamState *SS = State->get<StreamMap>(StreamSym);
1446 assert(SS->isOpened() &&
"Stream is expected to be opened");
1447 ClearErrorInNotFailed(StreamSym, SS);
1453 const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1454 for (
const auto &I : Map) {
1456 const StreamState &SS = I.second;
1458 ClearErrorInNotFailed(Sym, &SS);
1462 C.addTransition(StateNotFailed);
1463 C.addTransition(StateFailed);
1466void StreamChecker::evalClearerr(
const FnDescription *Desc,
1470 StreamOperationEvaluator E(
C);
1471 if (!E.Init(Desc,
Call,
C, State))
1475 State = E.setStreamState(
1477 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1478 C.addTransition(State);
1481void StreamChecker::evalFeofFerror(
const FnDescription *Desc,
1483 const StreamErrorState &ErrorKind)
const {
1485 StreamOperationEvaluator E(
C);
1486 if (!E.Init(Desc,
Call,
C, State))
1489 if (E.SS->ErrorState & ErrorKind) {
1494 C.addTransition(E.setStreamState(
1495 TrueState, StreamState::getOpened(Desc, ErrorKind,
1496 E.SS->FilePositionIndeterminate &&
1497 !ErrorKind.isFEof())));
1499 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1504 C.addTransition(E.setStreamState(
1506 StreamState::getOpened(
1507 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1511void StreamChecker::evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
1523 StreamOperationEvaluator E(
C);
1524 if (!E.Init(Desc,
Call,
C, State))
1528 State = State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1529 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(
Call));
1533 C.addTransition(State);
1536void StreamChecker::preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
1539 SVal StreamVal = getStreamArg(Desc,
Call);
1540 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1544 State = ensureStreamOpened(StreamVal,
C, State);
1548 C.addTransition(State);
1551void StreamChecker::evalSetFeofFerror(
const FnDescription *Desc,
1553 const StreamErrorState &ErrorKind,
1554 bool Indeterminate)
const {
1556 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1557 assert(StreamSym &&
"Operation not permitted on non-symbolic stream value.");
1558 const StreamState *SS = State->get<StreamMap>(StreamSym);
1559 assert(SS &&
"Stream should be tracked by the checker.");
1560 State = State->set<StreamMap>(
1562 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1563 C.addTransition(State);
1567StreamChecker::ensureStreamNonNull(
SVal StreamVal,
const Expr *StreamE,
1577 std::tie(StateNotNull, StateNull) = CM.
assumeDual(State, *Stream);
1579 if (!StateNotNull && StateNull) {
1581 auto R = std::make_unique<PathSensitiveBugReport>(
1582 BT_FileNull,
"Stream pointer might be NULL.", N);
1585 C.emitReport(std::move(R));
1590 return StateNotNull;
1600 const StreamState *SS = State->get<StreamMap>(Sym);
1604 if (SS->isClosed()) {
1609 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1611 "Stream might be already closed. Causes undefined behaviour.", N));
1618 if (SS->isOpenFailed()) {
1625 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1626 BT_UseAfterOpenFailed,
1627 "Stream might be invalid after "
1628 "(re-)opening it has failed. "
1629 "Can cause undefined behaviour.",
1640 static const char *BugMessage =
1641 "File position of the stream might be 'indeterminate' "
1642 "after a failed operation. "
1643 "Can cause undefined behavior.";
1649 const StreamState *SS = State->get<StreamMap>(Sym);
1653 assert(SS->isOpened() &&
"First ensure that stream is opened.");
1655 if (SS->FilePositionIndeterminate) {
1656 if (SS->ErrorState & ErrorFEof) {
1664 auto R = std::make_unique<PathSensitiveBugReport>(
1665 BT_IndeterminatePosition, BugMessage, N);
1666 R->markInteresting(Sym);
1667 C.emitReport(std::move(R));
1668 return State->set<StreamMap>(
1669 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof,
false));
1675 auto R = std::make_unique<PathSensitiveBugReport>(
1676 BT_IndeterminatePosition, BugMessage, N);
1677 R->markInteresting(Sym);
1678 C.emitReport(std::move(R));
1690 std::optional<nonloc::ConcreteInt> CI =
1695 int64_t X = CI->getValue().getSExtValue();
1696 if (
X == SeekSetVal ||
X == SeekCurVal ||
X == SeekEndVal)
1700 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1702 "The whence argument to fseek() should be "
1703 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1714 auto R = std::make_unique<PathSensitiveBugReport>(
1716 "Read function called when stream is in EOF state. "
1717 "Function has no effect.",
1719 R->markInteresting(StreamSym);
1720 C.emitReport(std::move(R));
1723 C.addTransition(State);
1729 ExplodedNode *Err =
C.generateNonFatalErrorNode(
C.getState(), Pred);
1745 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym,
C);
1746 assert(StreamOpenNode &&
"Could not find place of stream opening.");
1751 StreamStmt,
C.getSourceManager(),
1754 std::unique_ptr<PathSensitiveBugReport> R =
1755 std::make_unique<PathSensitiveBugReport>(
1757 "Opened stream never closed. Potential resource leak.", Err,
1758 LocUsedForUniqueing,
1760 R->markInteresting(LeakSym);
1761 C.emitReport(std::move(R));
1767void StreamChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1773 const StreamMapTy &Map = State->get<StreamMap>();
1774 for (
const auto &I : Map) {
1776 const StreamState &SS = I.second;
1777 if (!SymReaper.
isDead(Sym))
1780 LeakedSyms.push_back(Sym);
1781 State = State->remove<StreamMap>(Sym);
1785 if (!LeakedSyms.empty())
1786 N = reportLeaks(LeakedSyms,
C, N);
1788 C.addTransition(State, N);
1808 State = State->remove<StreamMap>(Sym);
1823bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
1832bool ento::shouldRegisterStreamTesterChecker(
const CheckerManager &Mgr) {
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, ArrayRef< unsigned int > EscapingArgs)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
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.
This represents one expression.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
A (possibly-)qualified type.
Stmt - This represents one statement.
bool isPointerType() const
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
const BugType & getBugType() const
An immutable map from CallDescriptions to arbitrary data.
const T * lookup(const CallEvent &Call) const
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.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
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.
void markNotInteresting(SymbolRef sym)
bool isInteresting(SymbolRef sym) const
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
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 - 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.
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
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)
std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)
Try to parse the value of a defined preprocessor macro.
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