balazske updated this revision to Diff 247824.
balazske marked 2 inline comments as done.
balazske added a comment.
Updated `StreamState` to include the error state.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D75356/new/
https://reviews.llvm.org/D75356
Files:
clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Index: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -28,23 +28,42 @@
namespace {
struct StreamState {
- enum Kind { Opened, Closed, OpenFailed, Escaped } K;
-
- StreamState(Kind k) : K(k) {}
-
- bool isOpened() const { return K == Opened; }
- bool isClosed() const { return K == Closed; }
- bool isOpenFailed() const { return K == OpenFailed; }
- // bool isEscaped() const { return K == Escaped; }
-
- bool operator==(const StreamState &X) const { return K == X.K; }
+ // State of a stream symbol.
+ // In any non-opened state the stream really does not exist.
+ // The OpenFailed means a previous open has failed, the stream is not open.
+ enum KindTy { Opened, Closed, OpenFailed } State;
+
+ // The error state of a stream.
+ // NoError: No error flag is set or stream is not open.
+ // EofError: EOF condition (feof returns true)
+ // OtherError: other (non-EOF) error (ferror returns true)
+ // AnyError: EofError or OtherError
+ enum ErrorKindTy {
+ NoError,
+ EofError,
+ OtherError,
+ AnyError
+ } ErrorState = NoError;
+
+ bool isOpened() const { return State == Opened; }
+ bool isClosed() const { return State == Closed; }
+ bool isOpenFailed() const { return State == OpenFailed; }
+
+ bool operator==(const StreamState &X) const {
+ return State == X.State && ErrorState == X.ErrorState;
+ }
- static StreamState getOpened() { return StreamState(Opened); }
- static StreamState getClosed() { return StreamState(Closed); }
- static StreamState getOpenFailed() { return StreamState(OpenFailed); }
- static StreamState getEscaped() { return StreamState(Escaped); }
+ static StreamState getOpened() { return StreamState{Opened}; }
+ static StreamState getClosed() { return StreamState{Closed}; }
+ static StreamState getOpenFailed() { return StreamState{OpenFailed}; }
+ static StreamState getOpenedWithError() {
+ return StreamState{Opened, AnyError};
+ }
- void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(State);
+ ID.AddInteger(ErrorState);
+ }
};
class StreamChecker;
@@ -69,6 +88,16 @@
return Call.getArgSVal(Desc->StreamArgNo);
}
+/// Create a conjured symbol return value for a call expression.
+DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
+ assert(CE && "Expecting a call expression.");
+
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+ return C.getSValBuilder()
+ .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
+ .castAs<DefinedSVal>();
+}
+
class StreamChecker
: public Checker<check::PreCall, eval::Call, check::DeadSymbols> {
mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
@@ -89,7 +118,7 @@
{&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
{{"fread", 4}, {&StreamChecker::preDefault, nullptr, 3}},
{{"fwrite", 4}, {&StreamChecker::preDefault, nullptr, 3}},
- {{"fseek", 3}, {&StreamChecker::preFseek, nullptr, 0}},
+ {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
{{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
{{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
@@ -102,15 +131,20 @@
void preDefault(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
- void preFseek(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const;
- void preFreopen(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const;
void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
+
+ void preFreopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
+
+ void preFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+ void evalFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
void evalFclose(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -156,6 +190,7 @@
} // end anonymous namespace
+// Store the state of the stream.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
void StreamChecker::checkPreCall(const CallEvent &Call,
@@ -191,48 +226,15 @@
C.addTransition(State);
}
-void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- SVal StreamVal = getStreamArg(Desc, Call);
- State = ensureStreamNonNull(StreamVal, C, State);
- if (!State)
- return;
- State = ensureStreamOpened(StreamVal, C, State);
- if (!State)
- return;
- State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
- if (!State)
- return;
-
- C.addTransition(State);
-}
-
-void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
- // Do not allow NULL as passed stream pointer but allow a closed stream.
- ProgramStateRef State = C.getState();
- State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State);
- if (!State)
- return;
-
- C.addTransition(State);
-}
-
void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- SValBuilder &SVB = C.getSValBuilder();
- const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
-
- auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
- DefinedSVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
- .castAs<DefinedSVal>();
+ DefinedSVal RetVal = makeRetVal(C, CE);
SymbolRef RetSym = RetVal.getAsSymbol();
- assert(RetSym && "RetVal must be a symbol here.");
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
@@ -249,6 +251,17 @@
C.addTransition(StateNull);
}
+void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ // Do not allow NULL as passed stream pointer but allow a closed stream.
+ ProgramStateRef State = C.getState();
+ State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State);
+ if (!State)
+ return;
+
+ C.addTransition(State);
+}
+
void StreamChecker::evalFreopen(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {
@@ -289,6 +302,61 @@
C.addTransition(StateRetNull);
}
+void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ State = ensureStreamNonNull(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureStreamOpened(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
+ if (!State)
+ return;
+
+ C.addTransition(State);
+}
+
+void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ // Ignore the call if the stream is is not tracked.
+ if (!State->get<StreamMap>(StreamSym))
+ return;
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+
+ // Make expression result.
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+
+ // Bifurcate the state into failed and non-failed.
+ // Return zero on success, nonzero on error.
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ // Reset the state to opened with no error.
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened());
+ // Set state to any error.
+ // Assume that EOF and other error is possible after the call.
+ StateFailed =
+ StateFailed->set<StreamMap>(StreamSym, StreamState::getOpenedWithError());
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits