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 {{{
"StreamTesterChecker_make_feof_stream"}, 1},
393 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4, ErrorFEof,
396 {{{
"StreamTesterChecker_make_ferror_stream"}, 1},
398 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
401 {{{
"StreamTesterChecker_make_ferror_indeterminate_stream"}, 1},
403 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
409 mutable std::optional<int> EofVal;
411 mutable int SeekSetVal = 0;
413 mutable int SeekCurVal = 1;
415 mutable int SeekEndVal = 2;
419 void evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
422 void preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
424 void evalFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
427 void evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
430 void preRead(
const FnDescription *Desc,
const CallEvent &
Call,
433 void preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
436 void evalFreadFwrite(
const FnDescription *Desc,
const CallEvent &
Call,
439 void evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
442 void evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
445 void evalFprintf(
const FnDescription *Desc,
const CallEvent &
Call,
448 void evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
451 void evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
454 void evalGetdelim(
const FnDescription *Desc,
const CallEvent &
Call,
457 void preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
459 void evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
462 void evalFgetpos(
const FnDescription *Desc,
const CallEvent &
Call,
465 void evalFsetpos(
const FnDescription *Desc,
const CallEvent &
Call,
468 void evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
471 void evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
474 void preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
477 void evalClearerr(
const FnDescription *Desc,
const CallEvent &
Call,
480 void evalFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
482 const StreamErrorState &ErrorKind)
const;
484 void evalSetFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
486 bool Indeterminate)
const;
488 void preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
491 void evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
494 void evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
546 for (
auto *
P :
Call.parameters()) {
549 T.getCanonicalType() != VaListType)
559 const std::string &Message)
const {
560 return C.getNoteTag([
this, StreamSym,
572 if (
const std::optional<int> OptInt =
577 if (
const std::optional<int> OptInt =
579 SeekSetVal = *OptInt;
580 if (
const std::optional<int> OptInt =
582 SeekEndVal = *OptInt;
583 if (
const std::optional<int> OptInt =
585 SeekCurVal = *OptInt;
589 VaListType =
C.getASTContext().getBuiltinVaListType().getCanonicalType();
599struct StreamOperationEvaluator {
604 const StreamState *SS =
nullptr;
606 StreamErrorState NewES;
609 : SVB(
C.getSValBuilder()), ACtx(
C.getASTContext()) {
615 StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
618 SS = State->get<StreamMap>(StreamSym);
621 NewES = SS->ErrorState;
622 CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
626 assertStreamStateOpened(SS);
631 bool isStreamEof()
const {
return SS->ErrorState == ErrorFEof; }
638 const StreamState &NewSS) {
639 NewES = NewSS.ErrorState;
640 return State->set<StreamMap>(StreamSym, NewSS);
645 return State->BindExpr(CE,
C.getLocationContext(), RetVal);
650 return State->BindExpr(CE,
C.getLocationContext(),
656 return State->BindExpr(CE,
C.getLocationContext(), Val);
661 return State->BindExpr(CE,
C.getLocationContext(),
662 C.getSValBuilder().makeNullWithType(CE->
getType()));
672 return State->assume(*Cond,
true);
678 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
679 return C.getConstraintManager().assumeDual(State, RetVal);
683 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
684 bool SetFerror = NewES.FError && !SS->ErrorState.FError;
685 if (SetFeof && !SetFerror)
686 return Ch->constructSetEofNoteTag(
C, StreamSym);
687 if (!SetFeof && SetFerror)
688 return Ch->constructSetErrorNoteTag(
C, StreamSym);
689 if (SetFeof && SetFerror)
690 return Ch->constructSetEofOrErrorNoteTag(
C, StreamSym);
703 if (!State->get<StreamMap>(StreamSym))
709 if (!State->get<StreamMap>(StreamSym))
721 const auto *CE =
Call.getOriginExpr();
724 EscapingVals.reserve(EscapingArgs.size());
725 for (
auto EscArgIdx : EscapingArgs)
726 EscapingVals.push_back(
Call.getArgSVal(EscArgIdx));
727 State = State->invalidateRegions(EscapingVals, CE,
C.blockCount(),
728 C.getLocationContext(),
742 const FnDescription *Desc = lookupFn(
Call);
743 if (!Desc || !Desc->PreFn)
746 Desc->PreFn(
this, Desc,
Call,
C);
750 const FnDescription *Desc = lookupFn(
Call);
751 if (!Desc && TestMode)
753 if (!Desc || !Desc->EvalFn)
756 Desc->EvalFn(
this, Desc,
Call,
C);
758 return C.isDifferent();
761void StreamChecker::evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
764 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
770 assert(RetSym &&
"RetVal must be a symbol here.");
772 State = State->BindExpr(CE,
C.getLocationContext(), RetVal);
777 std::tie(StateNotNull, StateNull) =
778 C.getConstraintManager().assumeDual(State, RetVal);
781 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
783 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
785 C.addTransition(StateNotNull,
786 constructLeakNoteTag(
C, RetSym,
"Stream opened here"));
787 C.addTransition(StateNull);
790void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
794 State = ensureStreamNonNull(getStreamArg(Desc,
Call),
795 Call.getArgExpr(Desc->StreamArgNo),
C, State);
799 C.addTransition(State);
802void StreamChecker::evalFreopen(
const FnDescription *Desc,
807 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
811 std::optional<DefinedSVal> StreamVal =
816 SymbolRef StreamSym = StreamVal->getAsSymbol();
823 if (!State->get<StreamMap>(StreamSym))
831 State->BindExpr(CE,
C.getLocationContext(), *StreamVal);
835 State->BindExpr(CE,
C.getLocationContext(),
836 C.getSValBuilder().makeNullWithType(CE->
getType()));
839 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
841 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
843 C.addTransition(StateRetNotNull,
844 constructLeakNoteTag(
C, StreamSym,
"Stream reopened here"));
845 C.addTransition(StateRetNull);
848void StreamChecker::evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
851 StreamOperationEvaluator E(
C);
852 if (!E.Init(Desc,
Call,
C, State))
858 State = E.setStreamState(State, StreamState::getClosed(Desc));
861 C.addTransition(E.bindReturnValue(State,
C, 0));
862 C.addTransition(E.bindReturnValue(State,
C, *EofVal));
865void StreamChecker::preRead(
const FnDescription *Desc,
const CallEvent &
Call,
868 SVal StreamVal = getStreamArg(Desc,
Call);
869 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
873 State = ensureStreamOpened(StreamVal,
C, State);
876 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
881 if (Sym && State->get<StreamMap>(Sym)) {
882 const StreamState *SS = State->get<StreamMap>(Sym);
883 if (SS->ErrorState & ErrorFEof)
884 reportFEofWarning(Sym,
C, State);
886 C.addTransition(State);
890void StreamChecker::preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
893 SVal StreamVal = getStreamArg(Desc,
Call);
894 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
898 State = ensureStreamOpened(StreamVal,
C, State);
901 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
905 C.addTransition(State);
908void StreamChecker::evalFreadFwrite(
const FnDescription *Desc,
910 bool IsFread)
const {
912 StreamOperationEvaluator E(
C);
913 if (!E.Init(Desc,
Call,
C, State))
916 std::optional<NonLoc> SizeVal =
Call.getArgSVal(1).getAs<
NonLoc>();
919 std::optional<NonLoc> NMembVal =
Call.getArgSVal(2).getAs<
NonLoc>();
928 if (State->isNull(*SizeVal).isConstrainedTrue() ||
929 State->isNull(*NMembVal).isConstrainedTrue()) {
932 C.addTransition(E.bindReturnValue(State,
C, 0));
938 if (IsFread && !E.isStreamEof())
943 if (!IsFread || !E.isStreamEof()) {
945 State->BindExpr(E.CE,
C.getLocationContext(), *NMembVal);
947 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
948 C.addTransition(StateNotFailed);
953 if (!IsFread && !PedanticMode)
958 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
959 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
963 StreamErrorState NewES;
965 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
970 StateFailed = E.setStreamState(
971 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
972 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
975void StreamChecker::evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
981 StreamOperationEvaluator E(
C);
982 if (!E.Init(Desc,
Call,
C, State))
985 if (!E.isStreamEof()) {
993 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
996 StateNotFailed = StateNotFailed->assumeInclusiveRange(
998 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
999 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1001 if (!StateNotFailed)
1003 C.addTransition(StateNotFailed);
1006 std::optional<DefinedSVal> GetBuf =
1011 State->BindExpr(E.CE,
C.getLocationContext(), *GetBuf);
1013 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1014 C.addTransition(StateNotFailed);
1021 StateFailed = E.bindReturnValue(State,
C, *EofVal);
1023 StateFailed = E.bindNullReturnValue(State,
C);
1027 StreamErrorState NewES =
1028 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1029 StateFailed = E.setStreamState(
1030 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1031 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1034void StreamChecker::evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
1040 StreamOperationEvaluator E(
C);
1041 if (!E.Init(Desc,
Call,
C, State))
1046 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<
NonLoc>();
1050 State->BindExpr(E.CE,
C.getLocationContext(), *PutVal);
1052 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1053 C.addTransition(StateNotFailed);
1058 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1060 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1061 if (!StateNotFailed)
1064 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1065 C.addTransition(StateNotFailed);
1074 StateFailed = E.setStreamState(
1075 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1076 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1079void StreamChecker::evalFprintf(
const FnDescription *Desc,
1082 if (
Call.getNumArgs() < 2)
1086 StreamOperationEvaluator E(
C);
1087 if (!E.Init(Desc,
Call,
C, State))
1091 State = State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1094 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1095 E.SVB.getConditionType())
1096 .getAs<DefinedOrUnknownSVal>();
1100 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1103 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1104 C.addTransition(StateNotFailed);
1111 StateFailed = E.setStreamState(
1112 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1113 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1116void StreamChecker::evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
1118 if (
Call.getNumArgs() < 2)
1122 StreamOperationEvaluator E(
C);
1123 if (!E.Init(Desc,
Call,
C, State))
1134 if (!E.isStreamEof()) {
1137 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1139 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1140 if (!StateNotFailed)
1143 if (
auto const *Callee =
Call.getCalleeIdentifier();
1146 for (
auto EscArg : llvm::seq(2u,
Call.getNumArgs()))
1147 EscArgs.push_back(EscArg);
1152 C.addTransition(StateNotFailed);
1162 StreamErrorState NewES =
1163 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1164 StateFailed = E.setStreamState(
1165 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1166 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1169void StreamChecker::evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
1172 StreamOperationEvaluator E(
C);
1173 if (!E.Init(Desc,
Call,
C, State))
1177 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<
NonLoc>();
1182 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1183 C.addTransition(StateNotFailed);
1192 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1193 C.addTransition(StateFailed);
1196void StreamChecker::evalGetdelim(
const FnDescription *Desc,
1200 StreamOperationEvaluator E(
C);
1201 if (!E.Init(Desc,
Call,
C, State))
1210 if (!E.isStreamEof()) {
1219 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1223 if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
1224 StateNotFailed = StateNotFailed->assume(
1229 SVal SizePtrSval =
Call.getArgSVal(1);
1231 if (NVal && isa<NonLoc>(*NVal)) {
1232 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1233 NVal->castAs<
NonLoc>(), RetVal);
1234 StateNotFailed = E.bindReturnValue(StateNotFailed,
C, RetVal);
1236 if (!StateNotFailed)
1238 C.addTransition(StateNotFailed);
1245 StreamErrorState NewES =
1246 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1247 StateFailed = E.setStreamState(
1248 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1251 StateFailed = StateFailed->bindLoc(*NewLinePtr,
UndefinedVal(),
1252 C.getLocationContext());
1253 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1256void StreamChecker::preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1259 SVal StreamVal = getStreamArg(Desc,
Call);
1260 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1264 State = ensureStreamOpened(StreamVal,
C, State);
1267 State = ensureFseekWhenceCorrect(
Call.getArgSVal(2),
C, State);
1271 C.addTransition(State);
1274void StreamChecker::evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1277 StreamOperationEvaluator E(
C);
1278 if (!E.Init(Desc,
Call,
C, State))
1285 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1286 C.addTransition(StateNotFailed);
1298 StateFailed = E.setStreamState(
1299 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1300 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1303void StreamChecker::evalFgetpos(
const FnDescription *Desc,
1307 StreamOperationEvaluator E(
C);
1308 if (!E.Init(Desc,
Call,
C, State))
1312 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1318 C.addTransition(StateNotFailed);
1319 C.addTransition(StateFailed);
1322void StreamChecker::evalFsetpos(
const FnDescription *Desc,
1326 StreamOperationEvaluator E(
C);
1327 if (!E.Init(Desc,
Call,
C, State))
1331 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1333 StateNotFailed = E.setStreamState(
1334 StateNotFailed, StreamState::getOpened(Desc, ErrorNone,
false));
1335 C.addTransition(StateNotFailed);
1344 StateFailed = E.setStreamState(
1345 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1347 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1350void StreamChecker::evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
1353 StreamOperationEvaluator E(
C);
1354 if (!E.Init(Desc,
Call,
C, State))
1359 State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1361 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1362 if (!StateNotFailed)
1370 C.addTransition(StateNotFailed);
1371 C.addTransition(StateFailed);
1374void StreamChecker::evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
1377 StreamOperationEvaluator E(
C);
1378 if (!E.Init(Desc,
Call,
C, State))
1382 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone,
false));
1383 C.addTransition(State);
1386void StreamChecker::preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1389 SVal StreamVal = getStreamArg(Desc,
Call);
1395 std::tie(StateNotNull, StateNull) =
1396 C.getConstraintManager().assumeDual(State, *Stream);
1397 if (StateNotNull && !StateNull)
1398 ensureStreamOpened(StreamVal,
C, StateNotNull);
1401void StreamChecker::evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1404 SVal StreamVal = getStreamArg(Desc,
Call);
1411 std::tie(StateNotNull, StateNull) =
1412 C.getConstraintManager().assumeDual(State, *Stream);
1413 if (StateNotNull && StateNull)
1415 if (StateNotNull && !StateNull)
1416 State = StateNotNull;
1420 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1429 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](
SymbolRef Sym,
1430 const StreamState *SS) {
1431 if (SS->ErrorState & ErrorFError) {
1432 StreamErrorState NewES =
1433 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1434 StreamState NewSS = StreamState::getOpened(Desc, NewES,
false);
1435 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1439 if (StateNotNull && !StateNull) {
1442 const StreamState *SS = State->get<StreamMap>(StreamSym);
1444 assert(SS->isOpened() &&
"Stream is expected to be opened");
1445 ClearErrorInNotFailed(StreamSym, SS);
1451 const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1452 for (
const auto &I : Map) {
1454 const StreamState &SS = I.second;
1456 ClearErrorInNotFailed(Sym, &SS);
1460 C.addTransition(StateNotFailed);
1461 C.addTransition(StateFailed);
1464void StreamChecker::evalClearerr(
const FnDescription *Desc,
1468 StreamOperationEvaluator E(
C);
1469 if (!E.Init(Desc,
Call,
C, State))
1473 State = E.setStreamState(
1475 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1476 C.addTransition(State);
1479void StreamChecker::evalFeofFerror(
const FnDescription *Desc,
1481 const StreamErrorState &ErrorKind)
const {
1483 StreamOperationEvaluator E(
C);
1484 if (!E.Init(Desc,
Call,
C, State))
1487 if (E.SS->ErrorState & ErrorKind) {
1492 C.addTransition(E.setStreamState(
1493 TrueState, StreamState::getOpened(Desc, ErrorKind,
1494 E.SS->FilePositionIndeterminate &&
1495 !ErrorKind.isFEof())));
1497 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1502 C.addTransition(E.setStreamState(
1504 StreamState::getOpened(
1505 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1509void StreamChecker::evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
1521 StreamOperationEvaluator E(
C);
1522 if (!E.Init(Desc,
Call,
C, State))
1526 State = State->BindExpr(E.CE,
C.getLocationContext(), RetVal);
1527 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(
Call));
1531 C.addTransition(State);
1534void StreamChecker::preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
1537 SVal StreamVal = getStreamArg(Desc,
Call);
1538 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1542 State = ensureStreamOpened(StreamVal,
C, State);
1546 C.addTransition(State);
1549void StreamChecker::evalSetFeofFerror(
const FnDescription *Desc,
1551 const StreamErrorState &ErrorKind,
1552 bool Indeterminate)
const {
1554 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1555 assert(StreamSym &&
"Operation not permitted on non-symbolic stream value.");
1556 const StreamState *SS = State->get<StreamMap>(StreamSym);
1557 assert(SS &&
"Stream should be tracked by the checker.");
1558 State = State->set<StreamMap>(
1560 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1561 C.addTransition(State);
1565StreamChecker::ensureStreamNonNull(
SVal StreamVal,
const Expr *StreamE,
1575 std::tie(StateNotNull, StateNull) = CM.
assumeDual(State, *Stream);
1577 if (!StateNotNull && StateNull) {
1579 auto R = std::make_unique<PathSensitiveBugReport>(
1580 BT_FileNull,
"Stream pointer might be NULL.", N);
1583 C.emitReport(std::move(R));
1588 return StateNotNull;
1598 const StreamState *SS = State->get<StreamMap>(Sym);
1602 if (SS->isClosed()) {
1607 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1609 "Stream might be already closed. Causes undefined behaviour.", N));
1616 if (SS->isOpenFailed()) {
1623 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1624 BT_UseAfterOpenFailed,
1625 "Stream might be invalid after "
1626 "(re-)opening it has failed. "
1627 "Can cause undefined behaviour.",
1638 static const char *BugMessage =
1639 "File position of the stream might be 'indeterminate' "
1640 "after a failed operation. "
1641 "Can cause undefined behavior.";
1647 const StreamState *SS = State->get<StreamMap>(Sym);
1651 assert(SS->isOpened() &&
"First ensure that stream is opened.");
1653 if (SS->FilePositionIndeterminate) {
1654 if (SS->ErrorState & ErrorFEof) {
1662 auto R = std::make_unique<PathSensitiveBugReport>(
1663 BT_IndeterminatePosition, BugMessage, N);
1664 R->markInteresting(Sym);
1665 C.emitReport(std::move(R));
1666 return State->set<StreamMap>(
1667 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof,
false));
1673 auto R = std::make_unique<PathSensitiveBugReport>(
1674 BT_IndeterminatePosition, BugMessage, N);
1675 R->markInteresting(Sym);
1676 C.emitReport(std::move(R));
1688 std::optional<nonloc::ConcreteInt> CI =
1693 int64_t X = CI->getValue().getSExtValue();
1694 if (
X == SeekSetVal ||
X == SeekCurVal ||
X == SeekEndVal)
1698 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1700 "The whence argument to fseek() should be "
1701 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1712 auto R = std::make_unique<PathSensitiveBugReport>(
1714 "Read function called when stream is in EOF state. "
1715 "Function has no effect.",
1717 R->markInteresting(StreamSym);
1718 C.emitReport(std::move(R));
1721 C.addTransition(State);
1727 ExplodedNode *Err =
C.generateNonFatalErrorNode(
C.getState(), Pred);
1743 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym,
C);
1744 assert(StreamOpenNode &&
"Could not find place of stream opening.");
1749 StreamStmt,
C.getSourceManager(),
1752 std::unique_ptr<PathSensitiveBugReport> R =
1753 std::make_unique<PathSensitiveBugReport>(
1755 "Opened stream never closed. Potential resource leak.", Err,
1756 LocUsedForUniqueing,
1758 R->markInteresting(LeakSym);
1759 C.emitReport(std::move(R));
1765void StreamChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1771 const StreamMapTy &Map = State->get<StreamMap>();
1772 for (
const auto &I : Map) {
1774 const StreamState &SS = I.second;
1775 if (!SymReaper.
isDead(Sym))
1778 LeakedSyms.push_back(Sym);
1779 State = State->remove<StreamMap>(Sym);
1783 if (!LeakedSyms.empty())
1784 N = reportLeaks(LeakedSyms,
C, N);
1786 C.addTransition(State, N);
1806 State = State->remove<StreamMap>(Sym);
1821bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
1830bool 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