[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
Alejandro Álvarez Ayllón via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 7 02:54:39 PST 2023
================
@@ -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 (State = ensureStreamOpened(StreamVal, C, StateNotNull))
+ C.addTransition(State);
+ 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();
+
+ // We will check the result even if the input is `NULL`,
+ // but do nothing if the input state is unknown.
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ const StreamState *SS;
+ 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->isOpened()) {
----------------
alejandro-alvarez-sonarsource wrote:
Unless I am mistaken, `SS` can be uninitialized here. If `StreamSym` is `NULL`, SS does not get initialized.
https://github.com/llvm/llvm-project/pull/74296
More information about the cfe-commits
mailing list