@@ -266,6 +266,9 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
266
266
{{{" fprintf" }},
267
267
{std::bind (&StreamChecker::preReadWrite, _1, _2, _3, _4, false ),
268
268
std::bind (&StreamChecker::evalFprintf, _1, _2, _3, _4), 0 }},
269
+ {{{" fscanf" }},
270
+ {std::bind (&StreamChecker::preReadWrite, _1, _2, _3, _4, true ),
271
+ std::bind (&StreamChecker::evalFscanf, _1, _2, _3, _4), 0 }},
269
272
{{{" ungetc" }, 2 },
270
273
{std::bind (&StreamChecker::preReadWrite, _1, _2, _3, _4, false ),
271
274
std::bind (&StreamChecker::evalUngetc, _1, _2, _3, _4), 1 }},
@@ -345,6 +348,9 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
345
348
void evalFprintf (const FnDescription *Desc, const CallEvent &Call,
346
349
CheckerContext &C) const ;
347
350
351
+ void evalFscanf (const FnDescription *Desc, const CallEvent &Call,
352
+ CheckerContext &C) const ;
353
+
348
354
void evalUngetc (const FnDescription *Desc, const CallEvent &Call,
349
355
CheckerContext &C) const ;
350
356
@@ -975,6 +981,69 @@ void StreamChecker::evalFprintf(const FnDescription *Desc,
975
981
C.addTransition (StateFailed);
976
982
}
977
983
984
+ void StreamChecker::evalFscanf (const FnDescription *Desc, const CallEvent &Call,
985
+ CheckerContext &C) const {
986
+ ProgramStateRef State = C.getState ();
987
+ if (Call.getNumArgs () < 2 )
988
+ return ;
989
+ SymbolRef StreamSym = getStreamArg (Desc, Call).getAsSymbol ();
990
+ if (!StreamSym)
991
+ return ;
992
+
993
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr ());
994
+ if (!CE)
995
+ return ;
996
+
997
+ const StreamState *OldSS = State->get <StreamMap>(StreamSym);
998
+ if (!OldSS)
999
+ return ;
1000
+
1001
+ assertStreamStateOpened (OldSS);
1002
+
1003
+ SValBuilder &SVB = C.getSValBuilder ();
1004
+ ASTContext &ACtx = C.getASTContext ();
1005
+
1006
+ // Add the success state.
1007
+ // In this context "success" means there is not an EOF or other read error
1008
+ // before any item is matched in 'fscanf'. But there may be match failure,
1009
+ // therefore return value can be 0 or greater.
1010
+ // It is not specified what happens if some items (not all) are matched and
1011
+ // then EOF or read error happens. Now this case is handled like a "success"
1012
+ // case, and no error flags are set on the stream. This is probably not
1013
+ // accurate, and the POSIX documentation does not tell more.
1014
+ if (OldSS->ErrorState != ErrorFEof) {
1015
+ NonLoc RetVal = makeRetVal (C, CE).castAs <NonLoc>();
1016
+ ProgramStateRef StateNotFailed =
1017
+ State->BindExpr (CE, C.getLocationContext (), RetVal);
1018
+ auto RetGeZero =
1019
+ SVB.evalBinOp (StateNotFailed, BO_GE, RetVal,
1020
+ SVB.makeZeroVal (ACtx.IntTy ), SVB.getConditionType ())
1021
+ .getAs <DefinedOrUnknownSVal>();
1022
+ if (!RetGeZero)
1023
+ return ;
1024
+ StateNotFailed = StateNotFailed->assume (*RetGeZero, true );
1025
+
1026
+ C.addTransition (StateNotFailed);
1027
+ }
1028
+
1029
+ // Add transition for the failed state.
1030
+ // Error occurs if nothing is matched yet and reading the input fails.
1031
+ // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
1032
+ // be set but it is not further specified if all are required to be set.
1033
+ // Documentation does not mention, but file position will be set to
1034
+ // indeterminate similarly as at 'fread'.
1035
+ ProgramStateRef StateFailed = bindInt (*EofVal, State, C, CE);
1036
+ StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof)
1037
+ ? ErrorFEof
1038
+ : ErrorNone | ErrorFEof | ErrorFError;
1039
+ StreamState NewSS = StreamState::getOpened (Desc, NewES, !NewES.isFEof ());
1040
+ StateFailed = StateFailed->set <StreamMap>(StreamSym, NewSS);
1041
+ if (OldSS->ErrorState != ErrorFEof)
1042
+ C.addTransition (StateFailed, constructSetEofNoteTag (C, StreamSym));
1043
+ else
1044
+ C.addTransition (StateFailed);
1045
+ }
1046
+
978
1047
void StreamChecker::evalUngetc (const FnDescription *Desc, const CallEvent &Call,
979
1048
CheckerContext &C) const {
980
1049
ProgramStateRef State = C.getState ();
0 commit comments