diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 61bf3c8528be2..be26f5521c8d7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -2204,6 +2204,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}), @@ -2212,6 +2222,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsEOF, 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(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + // int ungetc(int c, FILE *stream); addToFunctionSummaryMap( "ungetc", Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), @@ -2827,21 +2846,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 96072741a8abc..15986984802c0 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,7 +48,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/errno-stdlibraryfunctions.c b/clang/test/Analysis/errno-stdlibraryfunctions.c index 7876bafc2eb21..9b487fed0a2eb 100644 --- a/clang/test/Analysis/errno-stdlibraryfunctions.c +++ b/clang/test/Analysis/errno-stdlibraryfunctions.c @@ -103,3 +103,28 @@ void errno_execvp(char *File, char * Argv[]) { clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}} if (errno) {} // no warning } + +void errno_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 errno_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'}} + } +} diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c index 51b136d9ba356..03aa8e2e00a75 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)