[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:44:28 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/2] [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 c7449c5e8913af0028b31d7eef64daaf3860889a 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/2] [clang][analyzer] Support 'tello' and 'fseeko' in the
StreamChecker
---
.../StaticAnalyzer/Checkers/StreamChecker.cpp | 10 ++--
clang/test/Analysis/stream-error.c | 47 ++++++++++++-------
2 files changed, 34 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..ea246fcf1792cc 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,41 @@ 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}}
+ StreamTesterChecker_make_feof_stream(F);
+ rc = ftello(F);
+ clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}}
+ clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+ StreamTesterChecker_make_ferror_stream(F);
+ rc = ftello(F);
+ clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+ clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
fclose(F);
}
More information about the cfe-commits
mailing list