Szelethus added a comment.

I gave a lot of thought to the high level logic, but I need some time to really 
internalize whats happening here.

In D78374#1993858 <https://reviews.llvm.org/D78374#1993858>, @balazske wrote:

> Finally I had to make the decision to remove the `ErrorKindTy` enum and use 
> boolean flags instead for every possible error (including no error). This is 
> needed because we do not know always what error is possible if it is 
> "unknown". It could be determined from the last operation but now not any 
> more. The documentation for `fread` says that if 0 is passed as the size or 
> count argument the function returns zero and does not change the state. So 
> the error state before `fread` remains active. The new design is to have bool 
> values for every error kind (**feof** and **ferror** and a no-error state) so 
> multiple can be active in a state to indicate that more than one error state 
> is possible. If no-error or **feof** is possible these flags are turned on. 
> If we need to know in such a state if the stream is in **EOF** a state split 
> is needed to handle both cases (one with **EOF** and one with no-error). This 
> split must be done in the pre-call handler, for example if we want a warning 
> that the operation is not safe to use in **EOF** state. (But in this case 
> really no split is needed, only clear the EOF flag and make a warning.)


I guess one solution would be to have a history of last operations on the 
stream, but overall, it makes sense to have a make a shift to calculate the 
possible errors as we're evaluating the functions. Great idea!

> We can have another approach if we do not set return values of the functions 
> at all, instead save the symbol of the function and determine the returned 
> value later from the constraints actually applied on it. This may save state 
> splits, but only until a condition is encountered that checks for the 
> function's return value.

Or any stream modifying operation. If we don't have branches for the return 
value, that is almost a bug in itself. Also, digging the return value out of a 
branch condition may be difficult (see `ReturnValueChecker`). Lets stick with 
the state splits at the function call for now.



================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:222
         {StreamState::FEof, StreamState::FError, StreamState::NoError}}},
-      {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0, {}}},
+      // Note: ftell sets errno only.
+      {{"ftell", 1},
----------------
balazske wrote:
> Szelethus wrote:
> > Szelethus wrote:
> > > C'99 standard [[ 
> > > http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf | 
> > > §7.19.9.4.3]], about the `ftell function`:
> > > 
> > > > If successful, the `ftell` function returns the current value of the 
> > > > file position indicator for the stream. On failure, the `ftell` 
> > > > function returns `-1L` and stores an implementation-defined positive 
> > > > value in `errno`.
> > > 
> > > So we need an evalCall for this.
> > I mean, this function can only fail if the stream itself is an erroneous or 
> > closed state, right? So we can associate the -1 return value with the 
> > stream being in that, and the other with the stream being opened.
> The `ftell` is part of a later patch that is adding `ftell` (and probably 
> other functions). Only the "PossibleErrors" was updated in this patch because 
> its meaning was changed (it contains value even if only one error kind is 
> possible, before it was used only if at least two errors are possible).
> It is not sure how the file operations work, maybe the `ftell` needs access 
> to some data that is not available at the moment (and can fail even if the 
> stream was not OK). If the stream is already in failed state the question is: 
> Do ftell fail (then the we can make it return -1) or it does not fail but 
> gives an unusable value (then generate a checker warning and stop the 
> analysis).
In any case, this should be removed from the patch, because adding it seems to 
be an orthogonal change to this one.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:37
+/// Note: A stream has either FEOF or FERROR set but not both at the same time.
+struct StreamErrorState {
+  /// There is an execution path with none of the error flags set.
----------------
We're not describing the error state of a stream here, but rather //possible// 
error states, so the name should reflect that.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:207-210
+      {{"fread", 4},
+       {&StreamChecker::preFread,
+        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4,
+                  ErrorFEof | ErrorFError),
----------------
What does this mean? I guess not the possible states after an fread call, but 
rather the possible states after a **failed** fread call, but then...


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:225-228
       {{"feof", 1},
        {&StreamChecker::preDefault,
-        &StreamChecker::evalFeofFerror<StreamState::FEof>,
-        0,
-        {StreamState::FError, StreamState::NoError}}},
-      // Note: ferror can result in Unknown if at the call there is a
-      // PossibleErrors with all 3 error states (including NoError).
-      // Then if ferror is false the remaining error could be FEof or NoError.
+        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
+        0}},
----------------
...this doesn't make much sense, `feof` doesn't **cause** and error, it merely 
detects it.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:478
+
+  // FIXME: Check for stream in EOF state?
+
----------------
Will that be the next patch? :D Eager to see it!


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:512
+
+  Optional<NonLoc> SizeVal = Call.getArgSVal(1).castAs<NonLoc>();
+  if (!SizeVal)
----------------
`castAs` doesn't return an `Optional`.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp:533-539
+  ProgramStateRef StateNotFailed =
+      State->BindExpr(CE, C.getLocationContext(), *CountVal);
+  if (StateNotFailed) {
+    StateNotFailed =
+        StateNotFailed->set<StreamMap>(StreamSym, 
StreamState::getOpened(Desc));
+    C.addTransition(StateNotFailed);
+  }
----------------
This is where I'd really like to see a precise quote from the standard.

[[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf | C'99 standard]], 
§7.19.8.1.3, the return value of fread:

The fread function returns the number of elements successfully read, which may 
be
less than nmemb if a read error or end-of-file is encountered. If size or nmemb 
is zero,
fread returns zero and the contents of the array and the state of the stream 
remain
unchanged.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78374/new/

https://reviews.llvm.org/D78374



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to