29using namespace std::placeholders;
45struct StreamErrorState {
53 bool isNoError()
const {
return NoError && !FEof && !FError; }
54 bool isFEof()
const {
return !NoError && FEof && !FError; }
55 bool isFError()
const {
return !NoError && !FEof && FError; }
57 bool operator==(
const StreamErrorState &ES)
const {
58 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
61 bool operator!=(
const StreamErrorState &ES)
const {
return !(*
this == ES); }
63 StreamErrorState
operator|(
const StreamErrorState &E)
const {
64 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
67 StreamErrorState
operator&(
const StreamErrorState &E)
const {
68 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
71 StreamErrorState
operator~()
const {
return {!NoError, !FEof, !FError}; }
74 operator bool()
const {
return NoError || FEof || FError; }
76 void Profile(llvm::FoldingSetNodeID &ID)
const {
77 ID.AddBoolean(NoError);
79 ID.AddBoolean(FError);
83const StreamErrorState ErrorNone{
true,
false,
false};
84const StreamErrorState ErrorFEof{
false,
true,
false};
85const StreamErrorState ErrorFError{
false,
false,
true};
91 const FnDescription *LastOperation;
102 StreamErrorState
const ErrorState;
112 bool const FilePositionIndeterminate =
false;
114 StreamState(
const FnDescription *L, KindTy S,
const StreamErrorState &ES,
115 bool IsFilePositionIndeterminate)
116 : LastOperation(L), State(S), ErrorState(ES),
117 FilePositionIndeterminate(IsFilePositionIndeterminate) {
118 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
119 "FilePositionIndeterminate should be false in FEof case.");
120 assert((State == Opened || ErrorState.isNoError()) &&
121 "ErrorState should be None in non-opened stream state.");
124 bool isOpened()
const {
return State == Opened; }
125 bool isClosed()
const {
return State ==
Closed; }
126 bool isOpenFailed()
const {
return State == OpenFailed; }
131 return LastOperation ==
X.LastOperation && State ==
X.State &&
132 ErrorState ==
X.ErrorState &&
133 FilePositionIndeterminate ==
X.FilePositionIndeterminate;
136 static StreamState getOpened(
const FnDescription *L,
137 const StreamErrorState &ES = ErrorNone,
138 bool IsFilePositionIndeterminate =
false) {
139 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
141 static StreamState getClosed(
const FnDescription *L) {
142 return StreamState{L,
Closed, {},
false};
144 static StreamState getOpenFailed(
const FnDescription *L) {
145 return StreamState{L, OpenFailed, {},
false};
148 void Profile(llvm::FoldingSetNodeID &ID)
const {
149 ID.AddPointer(LastOperation);
150 ID.AddInteger(State);
151 ErrorState.Profile(ID);
152 ID.AddBoolean(FilePositionIndeterminate);
165using FnCheck = std::function<void(
const StreamChecker *,
const FnDescription *,
168using ArgNoTy =
unsigned int;
169static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
171struct FnDescription {
180 assert(Desc && Desc->StreamArgNo != ArgNone &&
181 "Try to get a non-existing stream argument.");
182 return Call.getArgSVal(Desc->StreamArgNo);
187 assert(CE &&
"Expecting a call expression.");
190 return C.getSValBuilder()
191 .conjureSymbolVal(
nullptr, CE, LCtx,
C.blockCount())
198 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
199 State = State->assume(RetVal,
true);
200 assert(State &&
"Assumption on new value should not fail.");
206 State = State->BindExpr(CE,
C.getLocationContext(),
211class StreamChecker :
public Checker<check::PreCall, eval::Call,
212 check::DeadSymbols, check::PointerEscape> {
213 BugType BT_FileNull{
this,
"NULL stream pointer",
"Stream handling error"};
214 BugType BT_UseAfterClose{
this,
"Closed stream",
"Stream handling error"};
215 BugType BT_UseAfterOpenFailed{
this,
"Invalid stream",
216 "Stream handling error"};
217 BugType BT_IndeterminatePosition{
this,
"Invalid stream state",
218 "Stream handling error"};
219 BugType BT_IllegalWhence{
this,
"Illegal whence argument",
220 "Stream handling error"};
221 BugType BT_StreamEof{
this,
"Stream already in EOF",
"Stream handling error"};
222 BugType BT_ResourceLeak{
this,
"Resource leak",
"Stream handling error",
235 bool TestMode =
false;
237 const BugType *getBT_StreamEof()
const {
return &BT_StreamEof; }
241 {{{
"fopen"}, 2}, {
nullptr, &StreamChecker::evalFopen, ArgNone}},
243 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
244 {{{
"tmpfile"}, 0}, {
nullptr, &StreamChecker::evalFopen, ArgNone}},
246 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
248 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
true),
249 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
true), 3}},
251 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
false),
252 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
false), 3}},
254 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
true),
255 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
257 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
true),
258 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
false), 2}},
260 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
false),
261 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
263 {std::bind(&StreamChecker::preReadWrite, _1,
_2, _3, _4,
false),
264 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
false), 1}},
266 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
268 {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
270 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
272 {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
274 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
276 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
278 {&StreamChecker::preDefault,
279 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFEof),
282 {&StreamChecker::preDefault,
283 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFError),
285 {{{
"fileno"}, 1}, {&StreamChecker::preDefault,
nullptr, 0}},
289 {{{
"StreamTesterChecker_make_feof_stream"}, 1},
291 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4, ErrorFEof),
293 {{{
"StreamTesterChecker_make_ferror_stream"}, 1},
295 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
301 mutable std::optional<int> EofVal;
303 mutable int SeekSetVal = 0;
305 mutable int SeekCurVal = 1;
307 mutable int SeekEndVal = 2;
309 void evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
312 void preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
314 void evalFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
317 void evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
320 void preReadWrite(
const FnDescription *Desc,
const CallEvent &
Call,
323 void evalFreadFwrite(
const FnDescription *Desc,
const CallEvent &
Call,
326 void evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
329 void evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
332 void preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
334 void evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
337 void evalFgetpos(
const FnDescription *Desc,
const CallEvent &
Call,
340 void evalFsetpos(
const FnDescription *Desc,
const CallEvent &
Call,
343 void evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
346 void evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
349 void preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
352 void evalClearerr(
const FnDescription *Desc,
const CallEvent &
Call,
355 void evalFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
357 const StreamErrorState &ErrorKind)
const;
359 void evalSetFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
361 const StreamErrorState &ErrorKind)
const;
412 if (!
Call.isGlobalCFunction())
414 for (
auto *
P :
Call.parameters()) {
426 const std::string &Message)
const {
427 return C.getNoteTag([
this, StreamSym,
444 return "Assuming stream reaches end-of-file here";
452 if (
const std::optional<int> OptInt =
457 if (
const std::optional<int> OptInt =
459 SeekSetVal = *OptInt;
460 if (
const std::optional<int> OptInt =
462 SeekEndVal = *OptInt;
463 if (
const std::optional<int> OptInt =
465 SeekCurVal = *OptInt;
483 assert(SS->isOpened() &&
"Stream is expected to be opened");
492 if (!State->get<StreamMap>(StreamSym))
498 if (!State->get<StreamMap>(StreamSym))
515 const FnDescription *Desc = lookupFn(
Call);
516 if (!Desc || !Desc->PreFn)
519 Desc->PreFn(
this, Desc,
Call,
C);
523 const FnDescription *Desc = lookupFn(
Call);
524 if (!Desc && TestMode)
526 if (!Desc || !Desc->EvalFn)
529 Desc->EvalFn(
this, Desc,
Call,
C);
531 return C.isDifferent();
534void StreamChecker::evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
537 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
543 assert(RetSym &&
"RetVal must be a symbol here.");
545 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
550 std::tie(StateNotNull, StateNull) =
551 C.getConstraintManager().assumeDual(State, RetVal);
554 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
556 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
558 C.addTransition(StateNotNull,
559 constructNoteTag(
C, RetSym,
"Stream opened here"));
560 C.addTransition(StateNull);
563void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
567 State = ensureStreamNonNull(getStreamArg(Desc,
Call),
568 Call.getArgExpr(Desc->StreamArgNo),
C, State);
572 C.addTransition(State);
575void StreamChecker::evalFreopen(
const FnDescription *Desc,
580 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
584 std::optional<DefinedSVal> StreamVal =
589 SymbolRef StreamSym = StreamVal->getAsSymbol();
596 if (!State->get<StreamMap>(StreamSym))
604 State->BindExpr(CE,
C.getLocationContext(), *StreamVal);
608 State->BindExpr(CE,
C.getLocationContext(),
609 C.getSValBuilder().makeNullWithType(CE->
getType()));
612 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
614 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
616 C.addTransition(StateRetNotNull,
617 constructNoteTag(
C, StreamSym,
"Stream reopened here"));
618 C.addTransition(StateRetNull);
621void StreamChecker::evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
628 const StreamState *SS = State->get<StreamMap>(Sym);
632 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
641 State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
646 CE,
C.getLocationContext(), SVB.makeIntVal(0,
C.getASTContext().IntTy));
648 State->BindExpr(CE,
C.getLocationContext(),
649 SVB.makeIntVal(*EofVal,
C.getASTContext().IntTy));
651 C.addTransition(StateSuccess);
652 C.addTransition(StateFailure);
655void StreamChecker::preReadWrite(
const FnDescription *Desc,
659 SVal StreamVal = getStreamArg(Desc,
Call);
660 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
664 State = ensureStreamOpened(StreamVal,
C, State);
667 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
672 C.addTransition(State);
677 if (Sym && State->get<StreamMap>(Sym)) {
678 const StreamState *SS = State->get<StreamMap>(Sym);
679 if (SS->ErrorState & ErrorFEof)
680 reportFEofWarning(Sym,
C, State);
682 C.addTransition(State);
686void StreamChecker::evalFreadFwrite(
const FnDescription *Desc,
688 bool IsFread)
const {
690 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
694 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
698 std::optional<NonLoc> SizeVal =
Call.getArgSVal(1).getAs<
NonLoc>();
701 std::optional<NonLoc> NMembVal =
Call.getArgSVal(2).getAs<
NonLoc>();
705 const StreamState *OldSS = State->get<StreamMap>(StreamSym);
717 if (State->isNull(*SizeVal).isConstrainedTrue() ||
718 State->isNull(*NMembVal).isConstrainedTrue()) {
721 State = bindInt(0, State,
C, CE);
722 C.addTransition(State);
728 if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
730 State->BindExpr(CE,
C.getLocationContext(), *NMembVal);
732 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
733 C.addTransition(StateNotFailed);
739 State->BindExpr(CE,
C.getLocationContext(), RetVal);
743 .getAs<DefinedOrUnknownSVal>();
746 StateFailed = StateFailed->assume(*Cond,
true);
750 StreamErrorState NewES;
753 (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
758 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
759 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
760 if (IsFread && OldSS->ErrorState != ErrorFEof)
761 C.addTransition(StateFailed, constructSetEofNoteTag(
C, StreamSym));
763 C.addTransition(StateFailed);
766void StreamChecker::evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
769 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
773 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
777 const StreamState *OldSS = State->get<StreamMap>(StreamSym);
786 if (OldSS->ErrorState != ErrorFEof) {
791 State->BindExpr(CE,
C.getLocationContext(), RetVal);
799 .getAs<DefinedOrUnknownSVal>();
807 .getAs<DefinedOrUnknownSVal>();
808 if (!CondLow || !CondHigh)
810 StateNotFailed = StateNotFailed->assume(*CondLow,
true);
813 StateNotFailed = StateNotFailed->assume(*CondHigh,
true);
816 C.addTransition(StateNotFailed);
819 std::optional<DefinedSVal> GetBuf =
824 State->BindExpr(CE,
C.getLocationContext(), *GetBuf);
825 StateNotFailed = StateNotFailed->set<StreamMap>(
826 StreamSym, StreamState::getOpened(Desc));
827 C.addTransition(StateNotFailed);
834 StateFailed = bindInt(*EofVal, State,
C, CE);
837 State->BindExpr(CE,
C.getLocationContext(),
838 C.getSValBuilder().makeNullWithType(CE->
getType()));
842 StreamErrorState NewES =
843 OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
844 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
845 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
846 if (OldSS->ErrorState != ErrorFEof)
847 C.addTransition(StateFailed, constructSetEofNoteTag(
C, StreamSym));
849 C.addTransition(StateFailed);
852void StreamChecker::evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
855 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
859 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
863 const StreamState *OldSS = State->get<StreamMap>(StreamSym);
874 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<
NonLoc>();
878 State->BindExpr(CE,
C.getLocationContext(), *PutVal);
880 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
881 C.addTransition(StateNotFailed);
886 State->BindExpr(CE,
C.getLocationContext(), RetVal);
888 auto &ASTC =
C.getASTContext();
891 .getAs<DefinedOrUnknownSVal>();
894 StateNotFailed = StateNotFailed->assume(*Cond,
true);
898 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
899 C.addTransition(StateNotFailed);
905 StreamState NewSS = StreamState::getOpened(Desc, ErrorFError,
true);
906 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
907 C.addTransition(StateFailed);
910void StreamChecker::preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
913 SVal StreamVal = getStreamArg(Desc,
Call);
914 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
918 State = ensureStreamOpened(StreamVal,
C, State);
921 State = ensureFseekWhenceCorrect(
Call.getArgSVal(2),
C, State);
925 C.addTransition(State);
928void StreamChecker::evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
931 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
935 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
940 if (!State->get<StreamMap>(StreamSym))
943 const llvm::APSInt *PosV =
944 C.getSValBuilder().getKnownValue(State,
Call.getArgSVal(1));
945 const llvm::APSInt *WhenceV =
946 C.getSValBuilder().getKnownValue(State,
Call.getArgSVal(2));
951 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
956 std::tie(StateFailed, StateNotFailed) =
957 C.getConstraintManager().assumeDual(State, RetVal);
961 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
966 StreamErrorState NewErrS = ErrorNone | ErrorFError;
968 if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
969 NewErrS = NewErrS | ErrorFEof;
970 StateFailed = StateFailed->set<StreamMap>(
971 StreamSym, StreamState::getOpened(Desc, NewErrS,
true));
973 C.addTransition(StateNotFailed);
974 C.addTransition(StateFailed, constructSetEofNoteTag(
C, StreamSym));
977void StreamChecker::evalFgetpos(
const FnDescription *Desc,
986 if (!State->get<StreamMap>(Sym))
989 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
994 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
996 std::tie(StateFailed, StateNotFailed) =
997 C.getConstraintManager().assumeDual(State, RetVal);
1002 C.addTransition(StateNotFailed);
1003 C.addTransition(StateFailed);
1006void StreamChecker::evalFsetpos(
const FnDescription *Desc,
1010 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1014 const StreamState *SS = State->get<StreamMap>(StreamSym);
1018 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1025 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
1027 std::tie(StateFailed, StateNotFailed) =
1028 C.getConstraintManager().assumeDual(State, RetVal);
1030 StateNotFailed = StateNotFailed->set<StreamMap>(
1031 StreamSym, StreamState::getOpened(Desc, ErrorNone,
false));
1037 StateFailed = StateFailed->set<StreamMap>(
1038 StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1040 C.addTransition(StateNotFailed);
1041 C.addTransition(StateFailed);
1044void StreamChecker::evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
1051 if (!State->get<StreamMap>(Sym))
1054 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1061 State->BindExpr(CE,
C.getLocationContext(), RetVal);
1062 auto Cond = SVB.
evalBinOp(State, BO_GE, RetVal,
1065 .getAs<DefinedOrUnknownSVal>();
1068 StateNotFailed = StateNotFailed->assume(*Cond,
true);
1069 if (!StateNotFailed)
1073 CE,
C.getLocationContext(), SVB.
makeIntVal(-1,
C.getASTContext().LongTy));
1078 C.addTransition(StateNotFailed);
1079 C.addTransition(StateFailed);
1082void StreamChecker::evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
1085 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1089 const StreamState *SS = State->get<StreamMap>(StreamSym);
1093 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1099 State = State->set<StreamMap>(StreamSym,
1100 StreamState::getOpened(Desc, ErrorNone,
false));
1102 C.addTransition(State);
1105void StreamChecker::evalClearerr(
const FnDescription *Desc,
1109 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1113 const StreamState *SS = State->get<StreamMap>(StreamSym);
1120 State = State->set<StreamMap>(
1122 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
1123 C.addTransition(State);
1126void StreamChecker::evalFeofFerror(
const FnDescription *Desc,
1128 const StreamErrorState &ErrorKind)
const {
1130 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1134 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1138 const StreamState *SS = State->get<StreamMap>(StreamSym);
1144 if (SS->ErrorState & ErrorKind) {
1149 C.addTransition(TrueState->set<StreamMap>(
1150 StreamSym, StreamState::getOpened(Desc, ErrorKind,
1151 SS->FilePositionIndeterminate &&
1152 !ErrorKind.isFEof())));
1154 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
1159 C.addTransition(FalseState->set<StreamMap>(
1161 StreamState::getOpened(
1162 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
1166void StreamChecker::preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
1169 SVal StreamVal = getStreamArg(Desc,
Call);
1170 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1174 State = ensureStreamOpened(StreamVal,
C, State);
1178 C.addTransition(State);
1181void StreamChecker::evalSetFeofFerror(
const FnDescription *Desc,
1183 const StreamErrorState &ErrorKind)
const {
1185 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1186 assert(StreamSym &&
"Operation not permitted on non-symbolic stream value.");
1187 const StreamState *SS = State->get<StreamMap>(StreamSym);
1188 assert(SS &&
"Stream should be tracked by the checker.");
1189 State = State->set<StreamMap>(
1190 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
1191 C.addTransition(State);
1195StreamChecker::ensureStreamNonNull(
SVal StreamVal,
const Expr *StreamE,
1205 std::tie(StateNotNull, StateNull) = CM.
assumeDual(State, *Stream);
1207 if (!StateNotNull && StateNull) {
1209 auto R = std::make_unique<PathSensitiveBugReport>(
1210 BT_FileNull,
"Stream pointer might be NULL.", N);
1213 C.emitReport(std::move(R));
1218 return StateNotNull;
1228 const StreamState *SS = State->get<StreamMap>(Sym);
1232 if (SS->isClosed()) {
1237 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1239 "Stream might be already closed. Causes undefined behaviour.", N));
1246 if (SS->isOpenFailed()) {
1253 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1254 BT_UseAfterOpenFailed,
1255 "Stream might be invalid after "
1256 "(re-)opening it has failed. "
1257 "Can cause undefined behaviour.",
1268 static const char *BugMessage =
1269 "File position of the stream might be 'indeterminate' "
1270 "after a failed operation. "
1271 "Can cause undefined behavior.";
1277 const StreamState *SS = State->get<StreamMap>(Sym);
1281 assert(SS->isOpened() &&
"First ensure that stream is opened.");
1283 if (SS->FilePositionIndeterminate) {
1284 if (SS->ErrorState & ErrorFEof) {
1292 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1293 BT_IndeterminatePosition, BugMessage, N));
1294 return State->set<StreamMap>(
1295 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof,
false));
1302 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1303 BT_IndeterminatePosition, BugMessage, N));
1314 std::optional<nonloc::ConcreteInt> CI =
1319 int64_t X = CI->getValue().getSExtValue();
1320 if (
X == SeekSetVal ||
X == SeekCurVal ||
X == SeekEndVal)
1324 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1326 "The whence argument to fseek() should be "
1327 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1338 auto R = std::make_unique<PathSensitiveBugReport>(
1340 "Read function called when stream is in EOF state. "
1341 "Function has no effect.",
1343 R->markInteresting(StreamSym);
1344 C.emitReport(std::move(R));
1347 C.addTransition(State);
1353 ExplodedNode *Err =
C.generateNonFatalErrorNode(
C.getState(), Pred);
1369 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym,
C);
1370 assert(StreamOpenNode &&
"Could not find place of stream opening.");
1375 StreamStmt,
C.getSourceManager(),
1378 std::unique_ptr<PathSensitiveBugReport> R =
1379 std::make_unique<PathSensitiveBugReport>(
1381 "Opened stream never closed. Potential resource leak.", Err,
1382 LocUsedForUniqueing,
1384 R->markInteresting(LeakSym);
1385 C.emitReport(std::move(R));
1391void StreamChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1397 const StreamMapTy &Map = State->get<StreamMap>();
1398 for (
const auto &I : Map) {
1400 const StreamState &SS = I.second;
1401 if (!SymReaper.
isDead(Sym))
1404 LeakedSyms.push_back(Sym);
1405 State = State->remove<StreamMap>(Sym);
1409 if (!LeakedSyms.empty())
1410 N = reportLeaks(LeakedSyms,
C, N);
1412 C.addTransition(State, N);
1432 State = State->remove<StreamMap>(Sym);
1445bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
1454bool 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.
void assertStreamStateOpened(const StreamState *SS)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
CanQualType UnsignedCharTy
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
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 llvm::APSInt & getMaxValue(const llvm::APSInt &v)
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.
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,...
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.
BasicValueFactory & getBasicValueFactory()
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.
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< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)
Try to parse the value of a defined preprocessor macro.
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)