balazske updated this revision to Diff 247814.
balazske added a comment.
Removed `MakeRetVal`, fixed a bug in evalFseek.
`evalFseek` is to be updated further.
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
@@ -47,6 +47,26 @@
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
};
+struct StreamErrorState {
+ // The error state of an opened stream.
+ // EofError: EOF condition (feof returns true)
+ // OtherError: other (non-EOF) error (ferror returns true)
+ // AnyError: EofError or OtherError
+ enum Kind { EofError, OtherError, AnyError } K;
+
+ StreamErrorState(Kind k) : K(k) {}
+
+ bool isEof() const { return K == EofError; }
+ bool isOther() const { return K == OtherError; }
+ bool isAny() const { return K == AnyError; }
+
+ bool operator==(const StreamErrorState &X) const { return K == X.K; }
+
+ static StreamErrorState getOther() { return StreamErrorState(OtherError); }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
+};
+
class StreamChecker;
struct FnDescription;
using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
@@ -69,6 +89,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 +119,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 +132,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,8 +191,14 @@
} // end anonymous namespace
+// Store the state of the stream.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
+// Store the error state of the stream.
+// Used only for streams in opened state.
+// The entry exists only if there is error for the stream.
+REGISTER_MAP_WITH_PROGRAMSTATE(StreamErrorMap, SymbolRef, StreamErrorState)
+
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
const FnDescription *Desc = lookupFn(Call);
@@ -191,48 +232,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 +257,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 {
@@ -269,6 +288,10 @@
if (!StreamSym)
return;
+ // Reset the error state.
+ // After reopen, the EOF and I/O error is not set.
+ State = State->remove<StreamErrorMap>(StreamSym);
+
// Generate state for non-failed case.
// Return value is the passed stream pointer.
// According to the documentations, the stream is closed first
@@ -289,6 +312,57 @@
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;
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+
+ // Clear error state.
+ State = State->remove<StreamErrorMap>(StreamSym);
+
+ // 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);
+
+ // Record the failed status, only if failed.
+ // fseek clears the EOF flag, sets only error flag.
+ StateFailed =
+ StateFailed->set<StreamErrorMap>(StreamSym, StreamErrorState::getOther());
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
@@ -300,6 +374,9 @@
if (!SS)
return;
+ // Clear error state.
+ State = State->remove<StreamErrorMap>(Sym);
+
// Close the File Descriptor.
// Regardless if the close fails or not, stream becomes "closed"
// and can not be used any more.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits