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 <benn...@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@-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; >From c7449c5e8913af0028b31d7eef64daaf3860889a Mon Sep 17 00:00:00 2001 From: Ben Shi <benn...@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@-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}} @@ -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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits