[clang] [clang][analyzer] Support 'tello' and 'fseeko' in the StreamChecker (PR #77580)

Ben Shi via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 10 17:58:18 PST 2024


https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/77580

>From cb79cad6837dba5d33476c65923ec714507a3fef Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Wed, 10 Jan 2024 19:00:27 +0800
Subject: [PATCH 1/3] [clang][analyzer] Support 'tello' and 'fseeko' in the
 StreamChecker

---
 .../StaticAnalyzer/Checkers/StreamChecker.cpp |  4 +
 .../Analysis/Inputs/system-header-simulator.h |  3 +
 clang/test/Analysis/stream-error.c            | 82 +++++++++++++++++++
 3 files changed, 89 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index fbfa101257d5e1..f6e6f3122f3aa7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -268,8 +268,12 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
         std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
       {{{"fseek"}, 3},
        {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
+      {{{"fseeko"}, 3},
+       {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
       {{{"ftell"}, 1},
        {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+      {{{"ftello"}, 1},
+       {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
       {{{"fflush"}, 1},
        {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
       {{{"rewind"}, 1},
diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h
index caae59c38a4c8e..cd7ac616bcc67f 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator.h
@@ -13,6 +13,7 @@ typedef __typeof(sizeof(int)) size_t;
 typedef long long __int64_t;
 typedef __int64_t __darwin_off_t;
 typedef __darwin_off_t fpos_t;
+typedef int off_t;
 
 typedef struct _FILE FILE;
 #define SEEK_SET 0 /* Seek from beginning of file. */
@@ -55,7 +56,9 @@ int fputc(int ch, FILE *stream);
 int fputs(const char *restrict s, FILE *restrict stream);
 int ungetc(int c, FILE *stream);
 int fseek(FILE *__stream, long int __off, int __whence);
+int fseeko(FILE *__stream, off_t __off, int __whence);
 long int ftell(FILE *__stream);
+off_t ftello(FILE *__stream);
 void rewind(FILE *__stream);
 int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
 int fsetpos(FILE *stream, const fpos_t *pos);
diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c
index c038348e799d29..1b14fc2eee2003 100644
--- a/clang/test/Analysis/stream-error.c
+++ b/clang/test/Analysis/stream-error.c
@@ -295,6 +295,37 @@ void error_fseek(void) {
   fclose(F);
 }
 
+void error_fseeko(void) {
+  FILE *F = fopen("file", "r");
+  if (!F)
+    return;
+  int rc = fseeko(F, 1, SEEK_SET);
+  if (rc) {
+    int IsFEof = feof(F), IsFError = ferror(F);
+    // Get feof or ferror or no error.
+    clang_analyzer_eval(IsFEof || IsFError);
+    // expected-warning at -1 {{FALSE}}
+    // expected-warning at -2 {{TRUE}}
+    clang_analyzer_eval(IsFEof && IsFError); // expected-warning {{FALSE}}
+    // Error flags should not change.
+    if (IsFEof)
+      clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}}
+    else
+      clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+    if (IsFError)
+      clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
+    else
+      clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  } else {
+    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+    // Error flags should not change.
+    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  }
+  fclose(F);
+}
+
 void error_fseek_0(void) {
   FILE *F = fopen("file", "r");
   if (!F)
@@ -324,6 +355,57 @@ void error_fseek_0(void) {
   fclose(F);
 }
 
+void error_fseeko_0(void) {
+  FILE *F = fopen("file", "r");
+  if (!F)
+    return;
+  int rc = fseeko(F, 0, SEEK_SET);
+  if (rc) {
+    int IsFEof = feof(F), IsFError = ferror(F);
+    // Get ferror or no error, but not feof.
+    clang_analyzer_eval(IsFError);
+    // expected-warning at -1 {{FALSE}}
+    // expected-warning at -2 {{TRUE}}
+    clang_analyzer_eval(IsFEof);
+    // expected-warning at -1 {{FALSE}}
+    // Error flags should not change.
+    clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+    if (IsFError)
+      clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
+    else
+      clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  } else {
+    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+    // Error flags should not change.
+    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  }
+  fclose(F);
+}
+
+void error_ftell(void) {
+  FILE *F = fopen("file", "r");
+  if (!F)
+    return;
+  long Ret = ftell(F);
+  if (!(Ret >= 0)) {
+    clang_analyzer_eval(Ret == -1L); // expected-warning {{TRUE}}
+  }
+  fclose(F);
+}
+
+void error_ftello(void) {
+  FILE *F = fopen("file", "r");
+  if (!F)
+    return;
+  off_t Ret = ftello(F);
+  if (!(Ret == -1)) {
+    clang_analyzer_eval(Ret >= 0);   // expected-warning {{TRUE}}
+  }
+  fclose(F);
+}
+
 void error_fflush_after_fclose(void) {
   FILE *F = tmpfile();
   int Ret;

>From ac08796261b3970d9a99c4ac6a2c0e7331620944 Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Thu, 11 Jan 2024 09:44:08 +0800
Subject: [PATCH 2/3] [clang][analyzer] Support 'tello' and 'fseeko' in the
 StreamChecker

---
 .../StaticAnalyzer/Checkers/StreamChecker.cpp | 10 ++---
 clang/test/Analysis/stream-error.c            | 39 ++++++++++---------
 2 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index f6e6f3122f3aa7..742426a628e065 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1117,10 +1117,10 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
   NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
   ProgramStateRef StateNotFailed =
       State->BindExpr(CE, C.getLocationContext(), RetVal);
-  auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
-                            SVB.makeZeroVal(C.getASTContext().LongTy),
-                            SVB.getConditionType())
-                  .getAs<DefinedOrUnknownSVal>();
+  auto Cond =
+      SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()),
+                    SVB.getConditionType())
+          .getAs<DefinedOrUnknownSVal>();
   if (!Cond)
     return;
   StateNotFailed = StateNotFailed->assume(*Cond, true);
@@ -1128,7 +1128,7 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
     return;
 
   ProgramStateRef StateFailed = State->BindExpr(
-      CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
+      CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType()));
 
   // This function does not affect the stream state.
   // Still we add success and failure state with the appropriate return value.
diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c
index 1b14fc2eee2003..5cd6ef4bb7424d 100644
--- a/clang/test/Analysis/stream-error.c
+++ b/clang/test/Analysis/stream-error.c
@@ -348,9 +348,6 @@ void error_fseek_0(void) {
   } else {
     clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
     clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
-    // Error flags should not change.
-    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
-    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
   }
   fclose(F);
 }
@@ -368,12 +365,6 @@ void error_fseeko_0(void) {
     // expected-warning at -2 {{TRUE}}
     clang_analyzer_eval(IsFEof);
     // expected-warning at -1 {{FALSE}}
-    // Error flags should not change.
-    clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
-    if (IsFError)
-      clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
-    else
-      clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
   } else {
     clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
     clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
@@ -388,21 +379,33 @@ void error_ftell(void) {
   FILE *F = fopen("file", "r");
   if (!F)
     return;
-  long Ret = ftell(F);
-  if (!(Ret >= 0)) {
-    clang_analyzer_eval(Ret == -1L); // expected-warning {{TRUE}}
-  }
+  long rc = ftell(F);
+  if (rc >= 0)
+    clang_analyzer_warnIfReached();          // expected-warning {{REACHABLE}}
+  else
+    clang_analyzer_eval(rc == -1);           // expected-warning {{TRUE}}
+  clang_analyzer_eval(feof(F) && ferror(F)); // expected-warning {{FALSE}}
+  StreamTesterChecker_make_feof_stream(F);
+  rc = ftell(F);
+  clang_analyzer_eval(feof(F));              // expected-warning {{TRUE}}
+  clang_analyzer_eval(ferror(F));            // expected-warning {{FALSE}}
+  StreamTesterChecker_make_ferror_stream(F);
+  rc = ftell(F);
+  clang_analyzer_eval(feof(F));              // expected-warning {{FALSE}}
+  clang_analyzer_eval(ferror(F));            // expected-warning {{TRUE}}
   fclose(F);
 }
 
 void error_ftello(void) {
-  FILE *F = fopen("file", "r");
+  FILE *F = tmpfile();
   if (!F)
     return;
-  off_t Ret = ftello(F);
-  if (!(Ret == -1)) {
-    clang_analyzer_eval(Ret >= 0);   // expected-warning {{TRUE}}
-  }
+  long rc = ftello(F);
+  if (rc >= 0)
+    clang_analyzer_warnIfReached();          // expected-warning {{REACHABLE}}
+  else
+    clang_analyzer_eval(rc == -1);           // expected-warning {{TRUE}}
+  clang_analyzer_eval(feof(F) && ferror(F)); // expected-warning {{FALSE}}
   fclose(F);
 }
 

>From 218f409bbb0bf4c870af1ef13f5b9dccd1ff13e0 Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Thu, 11 Jan 2024 09:57:56 +0800
Subject: [PATCH 3/3] [clang][analyzer] Support 'tello' and 'fseeko' in the
 StreamChecker

---
 clang/test/Analysis/stream-error.c | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c
index 5cd6ef4bb7424d..9e7408806dd6e6 100644
--- a/clang/test/Analysis/stream-error.c
+++ b/clang/test/Analysis/stream-error.c
@@ -307,21 +307,9 @@ void error_fseeko(void) {
     // expected-warning at -1 {{FALSE}}
     // expected-warning at -2 {{TRUE}}
     clang_analyzer_eval(IsFEof && IsFError); // expected-warning {{FALSE}}
-    // Error flags should not change.
-    if (IsFEof)
-      clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}}
-    else
-      clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
-    if (IsFError)
-      clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
-    else
-      clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
   } else {
     clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
     clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
-    // Error flags should not change.
-    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
-    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
   }
   fclose(F);
 }
@@ -368,9 +356,6 @@ void error_fseeko_0(void) {
   } else {
     clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
     clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
-    // Error flags should not change.
-    clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
-    clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
   }
   fclose(F);
 }



More information about the cfe-commits mailing list