-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][analyzer] Support 'fdopen' in the StreamChecker #76776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang-static-analyzer-1 @llvm/pr-subscribers-clang Author: Ben Shi (benshi001) ChangesFull diff: https://github.com/llvm/llvm-project/pull/76776.diff 8 Files Affected:
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@-1 {{Opened stream never closed. Potential resource leak}}
// expected-note@-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@-1 {{'F' is non-null}}
+ // expected-note@-2 {{Taking false branch}}
+ return;
+}
+// expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
+// expected-note@-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@-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)
|
@steakhal What is your opinion on this change ? |
One note that should be added to the documentation: |
which document file should I modify? Do you mean the release note ? |
Documentation is in checkers.rst but not accurate now. It must be updated with more information. |
No description provided.