24 #include "llvm/ADT/Sequence.h"
28 using namespace clang;
30 using namespace std::placeholders;
46 struct 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);
84 const StreamErrorState ErrorNone{
true,
false,
false};
85 const StreamErrorState ErrorFEof{
false,
true,
false};
86 const 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; }
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);
152 ErrorState.Profile(
ID);
153 ID.AddBoolean(FilePositionIndeterminate);
171 using FnCheck = std::function<void(
const StreamChecker *,
const FnDescription *,
174 using ArgNoTy =
unsigned int;
177 const char *FeofNote =
"Assuming stream reaches end-of-file here";
178 const char *FerrorNote =
"Assuming this stream operation fails";
180 struct FnDescription {
188 SVal getStreamArg(
const FnDescription *Desc,
const CallEvent &Call) {
189 assert(Desc && Desc->StreamArgNo != ArgNone &&
190 "Try to get a non-existing stream argument.");
191 return Call.getArgSVal(Desc->StreamArgNo);
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);
209 assert(
State &&
"Assumption on new value should not fail.");
215 State =
State->BindExpr(CE,
C.getLocationContext(),
220 inline void assertStreamStateOpened(
const StreamState *SS) {
221 assert(SS->isOpened() &&
"Stream is expected to be opened");
224 class StreamChecker :
public Checker<check::PreCall, eval::Call,
225 check::DeadSymbols, check::PointerEscape> {
226 BugType BT_FileNull{
this,
"NULL stream pointer",
"Stream handling error"};
227 BugType BT_UseAfterClose{
this,
"Closed stream",
"Stream handling error"};
228 BugType BT_UseAfterOpenFailed{
this,
"Invalid stream",
229 "Stream handling error"};
230 BugType BT_IndeterminatePosition{
this,
"Invalid stream state",
231 "Stream handling error"};
232 BugType BT_IllegalWhence{
this,
"Illegal whence argument",
233 "Stream handling error"};
234 BugType BT_StreamEof{
this,
"Stream already in EOF",
"Stream handling error"};
235 BugType BT_ResourceLeak{
this,
"Resource leak",
"Stream handling error",
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;
306 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
308 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
310 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
312 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
314 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
316 {&StreamChecker::preRead,
317 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
true), 3}},
319 {&StreamChecker::preWrite,
320 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
false), 3}},
322 {&StreamChecker::preRead,
323 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
325 {&StreamChecker::preRead,
326 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
false), 2}},
328 {&StreamChecker::preRead,
329 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
331 {&StreamChecker::preWrite,
332 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
334 {&StreamChecker::preWrite,
335 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
false), 1}},
337 {&StreamChecker::preWrite,
338 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
340 {&StreamChecker::preWrite,
341 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
343 {&StreamChecker::preWrite,
344 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
346 {&StreamChecker::preRead,
347 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
349 {&StreamChecker::preRead,
350 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
352 {&StreamChecker::preWrite,
353 std::bind(&StreamChecker::evalUngetc, _1,
_2, _3, _4), 1}},
355 {&StreamChecker::preRead,
356 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 3}},
358 {&StreamChecker::preRead,
359 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 2}},
361 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
363 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
365 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
367 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
369 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
371 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
373 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
375 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
377 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
379 {&StreamChecker::preDefault,
380 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFEof),
383 {&StreamChecker::preDefault,
384 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFError),
387 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
393 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4, ErrorFEof,
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,
545 const FnDescription *lookupFn(
const CallEvent &Call)
const {
548 for (
auto *
P :
Call.parameters()) {
551 T.getCanonicalType() != VaListType)
555 return FnDescriptions.
lookup(Call);
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();
601 struct 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(),
739 void StreamChecker::checkPreCall(
const CallEvent &Call,
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)
754 Desc = FnTestDescriptions.
lookup(Call);
755 if (!Desc || !Desc->EvalFn)
758 Desc->EvalFn(
this, Desc, Call, C);
760 return C.isDifferent();
763 void 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);
792 void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &Call,
796 State = ensureStreamNonNull(getStreamArg(Desc, Call),
797 Call.getArgExpr(Desc->StreamArgNo), C,
State);
804 void 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);
850 void 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));
867 void 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);
892 void 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);
910 void 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));
977 void 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));
1036 void 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));
1081 void 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));
1118 void 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);
1150 StateNotFailed =
escapeArgs(StateNotFailed, C, Call, EscArgs);
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));
1171 void 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);
1198 void 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));
1258 void 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);
1276 void 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));
1305 void 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);
1315 StateNotFailed =
escapeArgs(StateNotFailed, C, Call, {1});
1320 C.addTransition(StateNotFailed);
1321 C.addTransition(StateFailed);
1324 void 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));
1352 void 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);
1376 void 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));
1388 void 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);
1403 void 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);
1466 void 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));
1481 void 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())));
1511 void 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));
1536 void 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);
1551 void 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.");
1562 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1567 StreamChecker::ensureStreamNonNull(
SVal StreamVal,
const Expr *StreamE,
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));
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));
1767 void 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);
1785 if (!LeakedSyms.empty())
1786 N = reportLeaks(LeakedSyms, C, N);
1788 C.addTransition(
State, N);
1823 bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
1832 bool 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)
__DEVICE__ int max(int __a, int __b)
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
@ CLibrary
Match calls to functions from the C standard library.
@ SimpleFunc
Matches "simple" functions that are not methods.
Represents an abstract call to a function or method along a particular path.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
std::pair< ProgramStateRef, ProgramStateRef > ProgramStatePair
ExplodedNode * getFirstPred()
const ProgramStateRef & getState() const
const LocationContext * getLocationContext() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
The tag upon which the TagVisitor reacts.
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.
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
bool Init(InterpState &S, CodePtr OpPC)
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