[clang] [clang][analyzer] Improve modeling of 'popen' and 'pclose' in StdLibraryFunctionsChecker (PR #78895)

Ben Shi via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 21 02:30:12 PST 2024


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

None

>From b093d705ee949227ec0f7fe23bb65d43c7f9e51f Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Sun, 21 Jan 2024 18:29:06 +0800
Subject: [PATCH] [clang][analyzer] Improve modeling of 'popen' and 'pclose' in
 StdLibraryFunctionsChecker

---
 .../Checkers/StdLibraryFunctionsChecker.cpp   | 35 +++++++++++--------
 .../Analysis/Inputs/system-header-simulator.h |  2 ++
 .../Analysis/std-c-library-functions-POSIX.c  |  4 +--
 clang/test/Analysis/stream-errno.c            | 25 +++++++++++++
 4 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index d0eb5091444f6b..4053a829bd7a7d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -2202,6 +2202,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
             .ArgConstraint(NotNull(ArgNo(1)))
             .ArgConstraint(NotNull(ArgNo(2))));
 
+    // FILE *popen(const char *command, const char *type);
+    addToFunctionSummaryMap(
+        "popen",
+        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}),
+        Summary(NoEvalCall)
+            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+            .ArgConstraint(NotNull(ArgNo(0)))
+            .ArgConstraint(NotNull(ArgNo(1))));
+
     // int fclose(FILE *stream);
     addToFunctionSummaryMap(
         "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
@@ -2211,6 +2221,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
                   ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0))));
 
+    // int pclose(FILE *stream);
+    addToFunctionSummaryMap(
+        "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+        Summary(NoEvalCall)
+            .Case({ReturnValueCondition(WithinRange, {{0, IntMax}})},
+                  ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case({ReturnValueCondition(WithinRange, SingleValue(-1))},
+                  ErrnoNEZeroIrrelevant, GenericFailureMsg)
+            .ArgConstraint(NotNull(ArgNo(0))));
+
     // int ungetc(int c, FILE *stream);
     addToFunctionSummaryMap(
         "ungetc", Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}),
@@ -2827,21 +2847,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
             .ArgConstraint(
                 ArgumentCondition(0, WithinRange, Range(0, IntMax))));
 
-    // FILE *popen(const char *command, const char *type);
-    // FIXME: Improve for errno modeling.
-    addToFunctionSummaryMap(
-        "popen",
-        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}),
-        Summary(NoEvalCall)
-            .ArgConstraint(NotNull(ArgNo(0)))
-            .ArgConstraint(NotNull(ArgNo(1))));
-
-    // int pclose(FILE *stream);
-    // FIXME: Improve for errno modeling.
-    addToFunctionSummaryMap(
-        "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
-        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
-
     // int close(int fildes);
     addToFunctionSummaryMap(
         "close", Signature(ArgTypes{IntTy}, RetType{IntTy}),
diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h
index f8e3e546a7aed5..c41f22fef388e1 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator.h
@@ -47,7 +47,9 @@ 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);
+FILE *popen(const char *command, const char *mode);
 int fclose(FILE *fp);
+int pclose(FILE *stream);
 size_t fread(void *restrict, size_t, size_t, FILE *restrict);
 size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict);
 int fgetc(FILE *stream);
diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c
index 51b136d9ba3567..03aa8e2e00a75d 100644
--- a/clang/test/Analysis/std-c-library-functions-POSIX.c
+++ b/clang/test/Analysis/std-c-library-functions-POSIX.c
@@ -20,7 +20,9 @@
 // CHECK: Loaded summary for: FILE *fdopen(int fd, const char *mode)
 // CHECK: Loaded summary for: FILE *tmpfile(void)
 // CHECK: Loaded summary for: FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *restrict stream)
+// CHECK: Loaded summary for: FILE *popen(const char *command, const char *type)
 // CHECK: Loaded summary for: int fclose(FILE *stream)
+// CHECK: Loaded summary for: int pclose(FILE *stream)
 // CHECK: Loaded summary for: int fseek(FILE *stream, long offset, int whence)
 // CHECK: Loaded summary for: int fseeko(FILE *stream, off_t offset, int whence)
 // CHECK: Loaded summary for: off_t ftello(FILE *stream)
@@ -74,8 +76,6 @@
 // CHECK: Loaded summary for: DIR *opendir(const char *name)
 // CHECK: Loaded summary for: DIR *fdopendir(int fd)
 // CHECK: Loaded summary for: int isatty(int fildes)
-// CHECK: Loaded summary for: FILE *popen(const char *command, const char *type)
-// CHECK: Loaded summary for: int pclose(FILE *stream)
 // CHECK: Loaded summary for: int close(int fildes)
 // CHECK: Loaded summary for: long fpathconf(int fildes, int name)
 // CHECK: Loaded summary for: long pathconf(const char *path, int name)
diff --git a/clang/test/Analysis/stream-errno.c b/clang/test/Analysis/stream-errno.c
index fab6a58b3275a8..44bf949673082d 100644
--- a/clang/test/Analysis/stream-errno.c
+++ b/clang/test/Analysis/stream-errno.c
@@ -51,6 +51,17 @@ void check_freopen(void) {
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
 }
 
+void check_popen(void) {
+  FILE *F = popen("xxx", "r");
+  if (!F) {
+    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
+    if (errno) {}                    // no-warning
+  } else {
+    if (errno) {} // expected-warning{{An undefined value may be read from 'errno' [unix.Errno]}}
+    pclose(F);
+  }
+}
+
 void check_fclose(void) {
   FILE *F = tmpfile();
   if (!F)
@@ -64,6 +75,20 @@ void check_fclose(void) {
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
 }
 
+void check_pclose(void) {
+  FILE *F = popen("xx", "w");
+  if (!F)
+    return;
+  int Ret = pclose(F);
+  if (Ret == -1) {
+    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
+    if (errno) {}                    // no-warning
+  } else {
+    clang_analyzer_eval(Ret >= 0);   // expected-warning{{TRUE}}
+    if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+  }
+}
+
 void check_fread_size0(void) {
   char Buf[10];
   FILE *F = tmpfile();



More information about the cfe-commits mailing list