Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from Jan 11, 2024
Merged

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

merged 5 commits into from Jan 11, 2024

Conversation

benshi001
Copy link
Member

No description provided.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Jan 10, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 10, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ben Shi (benshi001)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/77580.diff

3 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (+4)
  • (modified) clang/test/Analysis/Inputs/system-header-simulator.h (+3)
  • (modified) clang/test/Analysis/stream-error.c (+82)
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@-1 {{FALSE}}
+    // expected-warning@-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@-1 {{FALSE}}
+    // expected-warning@-2 {{TRUE}}
+    clang_analyzer_eval(IsFEof);
+    // expected-warning@-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;

@balazske
Copy link
Collaborator

It is better to replace in code of evalFtell the C.getASTContext().LongTy with Call.getResultType() (occurs 2 times), this is more exact if off_t is defined to something else than long.

@benshi001
Copy link
Member Author

It is better to replace in code of evalFtell the C.getASTContext().LongTy with Call.getResultType() (occurs 2 times), this is more exact if off_t is defined to something else than long.

This has been fixed. Thanks!

@benshi001 benshi001 merged commit 19081f4 into llvm:main Jan 11, 2024
3 of 4 checks passed
@benshi001 benshi001 deleted the csa-o branch January 11, 2024 10:48
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants