[PATCH] D75851: [Analyzer][StreamChecker] Added evaluation of fseek.

Balázs Kéri via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 9 09:08:52 PDT 2020


balazske created this revision.
Herald added subscribers: cfe-commits, martong, Charusso, gamesh411, dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, xazax.hun.
Herald added a reviewer: Szelethus.
Herald added a project: clang.
balazske added a parent revision: D75682: [Analyzer][StreamChecker] Introduction of stream error handling..

Function `fseek` is now evaluated with setting error return value
and error flags.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D75851

Files:
  clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
  clang/test/Analysis/stream-error.c


Index: clang/test/Analysis/stream-error.c
===================================================================
--- clang/test/Analysis/stream-error.c
+++ clang/test/Analysis/stream-error.c
@@ -38,3 +38,20 @@
   }
   fclose(F);
 }
+
+void error_fseek() {
+  FILE *F = fopen("file", "r");
+  if (!F)
+    return;
+  int rc = fseek(F, 0, SEEK_SET);
+  if (rc) {
+    int Eof = feof(F), Error = ferror(F);
+    clang_analyzer_eval(Eof || Error); // expected-warning {{FALSE}} \
+                                       // expected-warning {{TRUE}}
+    clang_analyzer_eval(Eof && Error); // expected-warning {{FALSE}}
+  } else {
+    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  }
+  fclose(F);
+}
Index: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -128,7 +128,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}},
@@ -154,6 +154,8 @@
 
   void preFseek(const FnDescription *Desc, const CallEvent &Call,
                 CheckerContext &C) const;
+  void evalFseek(const FnDescription *Desc, const CallEvent &Call,
+                 CheckerContext &C) const;
 
   void preDefault(const FnDescription *Desc, const CallEvent &Call,
                   CheckerContext &C) const;
@@ -340,6 +342,48 @@
   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 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());
+  // There are two ways of failure:
+  // We get some error (ferror or feof).
+  ProgramStateRef StateFailedWithFError = StateFailed->set<StreamMap>(
+      StreamSym, StreamState::getOpenedWithAnyError());
+  // We get none of the error flags.
+  ProgramStateRef StateFailedWithoutFError =
+      StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened());
+
+  C.addTransition(StateNotFailed);
+  C.addTransition(StateFailedWithFError);
+  C.addTransition(StateFailedWithoutFError);
+}
+
 void StreamChecker::evalClearerr(const FnDescription *Desc,
                                  const CallEvent &Call,
                                  CheckerContext &C) const {


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D75851.249118.patch
Type: text/x-patch
Size: 3698 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200309/86f0cfc3/attachment-0001.bin>


More information about the cfe-commits mailing list