[clang] [clang][analyzer] Support 'fdopen' in the StreamChecker (PR #76776)

Ben Shi via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 2 19:31:36 PST 2024


https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/76776

None

>From 8a7caba467e121290d36ad75626ea95ea47b41f2 Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Wed, 3 Jan 2024 11:30:57 +0800
Subject: [PATCH] [clang][analyzer] Support 'fdopen' in the StreamChecker

---
 clang/docs/ReleaseNotes.rst                         |  2 +-
 clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp |  1 +
 .../test/Analysis/Inputs/system-header-simulator.h  |  1 +
 clang/test/Analysis/stream-error.c                  | 13 +++++++++++--
 clang/test/Analysis/stream-non-posix-function.c     |  7 ++++++-
 clang/test/Analysis/stream-note.c                   | 10 ++++++++++
 clang/test/Analysis/stream-stdlibraryfunctionargs.c |  8 ++++++++
 clang/test/Analysis/stream.c                        |  7 +++++++
 8 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0c8fec691bf3c9..2be7f66c2e8081 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1158,7 +1158,7 @@ Improvements
   `0954dc3fb921 <https://github.com/llvm/llvm-project/commit/0954dc3fb9214b994623f5306473de075f8e3593>`_)
 
 - Improved the ``alpha.unix.Stream`` checker by modeling more functions like,
-  ``fflush``, ``fputs``, ``fgetc``, ``fputc``, ``fopen``, ``fopen``, ``fgets``.
+  ``fflush``, ``fputs``, ``fgetc``, ``fputc``, ``fopen``, ``fdopen``, ``fgets``, ``tmpfile``.
   (`#74296 <https://github.com/llvm/llvm-project/pull/74296>`_,
   `#73335 <https://github.com/llvm/llvm-project/pull/73335>`_,
   `#72627 <https://github.com/llvm/llvm-project/pull/72627>`_,
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 254b36ed03968d..25da3c18e8519f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -239,6 +239,7 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
 private:
   CallDescriptionMap<FnDescription> FnDescriptions = {
       {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+      {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
       {{{"freopen"}, 3},
        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
       {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h
index 409a969a0d4cce..8c43c48c6a3e45 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator.h
@@ -43,6 +43,7 @@ FILE *funopen(const void *,
               int (*)(void *));
 
 FILE *fopen(const char *restrict path, const char *restrict mode);
+FILE *fdopen(int fd, const char *mode);
 FILE *tmpfile(void);
 FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *restrict stream);
 int fclose(FILE *fp);
diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c
index 37e1e54dfc89d5..13c6684b5840af 100644
--- a/clang/test/Analysis/stream-error.c
+++ b/clang/test/Analysis/stream-error.c
@@ -21,6 +21,15 @@ void error_fopen(void) {
   fclose(F);
 }
 
+void error_fdopen(int fd) {
+  FILE *F = fdopen(fd, "r");
+  if (!F)
+    return;
+  clang_analyzer_eval(feof(F));   // expected-warning {{FALSE}}
+  clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+  fclose(F);
+}
+
 void error_freopen(void) {
   FILE *F = fopen("file", "r");
   if (!F)
@@ -146,8 +155,8 @@ void error_fgets(void) {
   fgets(Buf, sizeof(Buf), F);         // expected-warning {{Stream might be already closed}}
 }
 
-void error_fputc(void) {
-  FILE *F = tmpfile();
+void error_fputc(int fd) {
+  FILE *F = fdopen(fd, "w");
   if (!F)
     return;
   int Ret = fputc('X', F);
diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c
index c6dc65afe788bb..091d95a573ddf9 100644
--- a/clang/test/Analysis/stream-non-posix-function.c
+++ b/clang/test/Analysis/stream-non-posix-function.c
@@ -8,11 +8,16 @@ typedef struct _FILE FILE;
 // These functions are not standard C library functions.
 FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters.
 FILE *fopen(const char *restrict path);   // Real 'fopen' should have exactly 2 formal parameters.
+FILE *fdopen(int fd);                     // Real 'fdopen' should have exactly 2 formal parameters.
 
 void test_fopen_non_posix(void) {
   FILE *fp = fopen("file"); // no-leak: This isn't the standard POSIX `fopen`, we don't know the semantics of this call.
 }
 
 void test_tmpfile_non_posix(void) {
-  FILE *fp = tmpfile("file"); // // no-leak: This isn't the standard POSIX `tmpfile`, we don't know the semantics of this call.
+  FILE *fp = tmpfile("file"); // no-leak: This isn't the standard POSIX `tmpfile`, we don't know the semantics of this call.
+}
+
+void test_fdopen_non_posix(int fd) {
+  FILE *fp = fdopen(fd); // no-leak: This isn't the standard POSIX `fdopen`, we don't know the semantics of this call.
 }
diff --git a/clang/test/Analysis/stream-note.c b/clang/test/Analysis/stream-note.c
index b9fdc16b19e55e..e412015eb68393 100644
--- a/clang/test/Analysis/stream-note.c
+++ b/clang/test/Analysis/stream-note.c
@@ -54,6 +54,16 @@ void check_note_freopen(void) {
 // expected-warning at -1 {{Opened stream never closed. Potential resource leak}}
 // expected-note at -2 {{Opened stream never closed. Potential resource leak}}
 
+void check_note_fdopen(int fd) {
+  FILE *F = fdopen(fd, "r"); // expected-note {{Stream opened here}}
+  if (!F)
+    // expected-note at -1 {{'F' is non-null}}
+    // expected-note at -2 {{Taking false branch}}
+    return;
+}
+// expected-warning at -1 {{Opened stream never closed. Potential resource leak}}
+// expected-note at -2 {{Opened stream never closed. Potential resource leak}}
+
 void check_note_leak_2(int c) {
   FILE *F1 = fopen("foo1.c", "r"); // expected-note {{Stream opened here}}
   // stdargs-note at -1 {{'fopen' is successful}}
diff --git a/clang/test/Analysis/stream-stdlibraryfunctionargs.c b/clang/test/Analysis/stream-stdlibraryfunctionargs.c
index 938901ec088291..0053510163efc0 100644
--- a/clang/test/Analysis/stream-stdlibraryfunctionargs.c
+++ b/clang/test/Analysis/stream-stdlibraryfunctionargs.c
@@ -23,6 +23,14 @@ void test_fopen(void) {
   // stdfunc-warning{{should not be NULL}}
 }
 
+void test_fdopen(int fd) {
+  FILE *fp = fdopen(fd, "r");
+  clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}}
+  fclose(fp); // \
+  // stream-warning{{Stream pointer might be NULL}} \
+  // stdfunc-warning{{should not be NULL}}
+}
+
 void test_tmpfile(void) {
   FILE *fp = tmpfile();
   clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}}
diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c
index 0b655dbdc571fe..060d561c1fe1c2 100644
--- a/clang/test/Analysis/stream.c
+++ b/clang/test/Analysis/stream.c
@@ -102,6 +102,13 @@ void f_open(void) {
   fclose(p);
 }
 
+void f_dopen(int fd) {
+  FILE *F = fdopen(fd, "r");
+  char buf[1024];
+  fread(buf, 1, 1, F); // expected-warning {{Stream pointer might be NULL}}
+  fclose(F);
+}
+
 void f_seek(void) {
   FILE *p = fopen("foo", "r");
   if (!p)



More information about the cfe-commits mailing list