https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/74296
None >From 2929f07a9ac2c462bf7aed9fe10307ef79659074 Mon Sep 17 00:00:00 2001 From: Ben Shi <benn...@tencent.com> Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 16 ++++++++++++++++ .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index a4799b5f762ca..5744feba4d35b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public Checker<check::PreCall, eval::Call, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, {{{"ftell"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, + {{{"fflush"}, 1}, {&StreamChecker::preFflush, nullptr, 0}}, {{{"rewind"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, {{{"fgetpos"}, 2}, @@ -360,6 +361,9 @@ class StreamChecker : public Checker<check::PreCall, eval::Call, CheckerContext &C, const StreamErrorState &ErrorKind) const; + void preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + /// Check that the stream (in StreamVal) is not NULL. /// If it can only be NULL a fatal error is emitted and nullptr returned. /// Otherwise the return value is a new state where the stream is constrained @@ -1188,6 +1192,18 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, C.addTransition(State); } +void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + // Skip if the stream is NULL/nullptr, which means flush all streams. + if (!Call.getArgExpr(Desc->StreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { + ProgramStateRef State = C.getState(); + if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) + return; + fclose(F); + fflush(F); // expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits