[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)

Ben Shi via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 4 01:49:25 PST 2023


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 <bennshi at 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)



More information about the cfe-commits mailing list