================ @@ -1191,6 +1199,84 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, C.addTransition(State); } +void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); + SymbolRef StreamSym = StreamVal.getAsSymbol(); + if (!Stream || !StreamSym) + return; + + ProgramStateRef StateNotNull, StateNull; + std::tie(StateNotNull, StateNull) = + C.getConstraintManager().assumeDual(State, *Stream); + if (StateNotNull) { + if (StateNotNull = ensureStreamOpened(StreamVal, C, StateNotNull)) + C.addTransition(StateNotNull); + } else if (StateNull) { + const StreamState *SS = StateNull->get<StreamMap>(StreamSym); + if (!SS || !SS->isOpenFailed()) { + StateNull = StateNull->set<StreamMap>(StreamSym, + StreamState::getOpenFailed(Desc)); + if (StateNull) + C.addTransition(StateNull); + } + } +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + const StreamState *SS = nullptr; + + // Skip if the input stream's state is unknown. + // FIXME: We should skip non-NULL constant, such as `(FILE *) 0x1234`. + if (StreamSym) { + if (!(SS = State->get<StreamMap>(StreamSym))) + return; + assert((SS->isOpened() || SS->isOpenFailed()) && + "Stream is expected to opened or open-failed"); + } + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + // `fflush` returns EOF on failure, otherwise returns 0. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + C.addTransition(StateFailed); + + // Clear error states if `fflush` returns 0, but retain their EOF flags. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + auto ClearError = [&StateNotFailed, Desc](SymbolRef Sym, + const StreamState *SS) { + if (SS->ErrorState & ErrorFError) { + StreamErrorState NewES = + (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; + StreamState NewSS = StreamState::getOpened(Desc, NewES, false); + StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); + } + }; + + if (SS && SS->isOpened()) { ---------------- balazske wrote:
This decision can not be made with `SS`. If a stream pointer is known to be non-null but unknown to the checker, `SS` is null, but not NULL is passed to `fflush`. The check can be made with assumptions on `getStreamArg(Desc, Call)`. If it can be both NULL and non-NULL, we can't do anything in the `evalCall` (for the same reason as in `preFflush`), otherwise we can get if it is clearly NULL or not NULL. https://github.com/llvm/llvm-project/pull/74296 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits