[clang] db4d5f7 - [analyzer][StdLibraryFunctionsChecker] Add POSIX file handling functions
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 2 05:28:46 PDT 2020
Author: Gabor Marton
Date: 2020-07-02T14:28:05+02:00
New Revision: db4d5f7048a26a7708821e46095742aecfd8ba46
URL: https://github.com/llvm/llvm-project/commit/db4d5f7048a26a7708821e46095742aecfd8ba46
DIFF: https://github.com/llvm/llvm-project/commit/db4d5f7048a26a7708821e46095742aecfd8ba46.diff
LOG: [analyzer][StdLibraryFunctionsChecker] Add POSIX file handling functions
Adding file handling functions from the POSIX standard (2017).
A new checker option is introduced to enable them.
In follow-up patches I am going to upstream networking, pthread, and other
groups of POSIX functions.
Differential Revision: https://reviews.llvm.org/D82288
Added:
clang/test/Analysis/std-c-library-functions-POSIX.c
Modified:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
clang/test/Analysis/analyzer-config.c
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 8430dc123598..0ceedfb8771b 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -354,7 +354,14 @@ def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">,
"If set to true, the checker displays the found summaries "
"for the given translation unit.",
"false",
- Released>
+ Released,
+ Hide>,
+ CmdLineOption<Boolean,
+ "ModelPOSIX",
+ "If set to true, the checker models functions from the "
+ "POSIX standard.",
+ "false",
+ InAlpha>
]>,
Documentation<NotDocumented>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 6feae56502f1..29393c2ca02b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -461,6 +461,7 @@ class StdLibraryFunctionsChecker
CheckerNameRef CheckNames[CK_NumCheckKinds];
bool DisplayLoadedSummaries = false;
+ bool ModelPOSIX = false;
private:
Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
@@ -736,13 +737,18 @@ llvm::Optional<QualType> lookupType(StringRef Name, const ASTContext &ACtx) {
// typedef struct FILE FILE;
// In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and
// we have a TypedefDecl with the name 'FILE'.
- for (Decl *D : LookupRes) {
+ for (Decl *D : LookupRes)
if (auto *TD = dyn_cast<TypedefNameDecl>(D))
return ACtx.getTypeDeclType(TD).getCanonicalType();
- }
- assert(LookupRes.size() == 1 && "Type identifier should be unique");
- auto *D = cast<TypeDecl>(LookupRes.front());
- return ACtx.getTypeDeclType(D).getCanonicalType();
+
+ // Find the first TypeDecl.
+ // There maybe cases when a function has the same name as a struct.
+ // E.g. in POSIX: `struct stat` and the function `stat()`:
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ for (Decl *D : LookupRes)
+ if (auto *TD = dyn_cast<TypeDecl>(D))
+ return ACtx.getTypeDeclType(TD).getCanonicalType();
+ return None;
}
void StdLibraryFunctionsChecker::initFunctionSummaries(
@@ -761,26 +767,46 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// of function summary for common cases (eg. ssize_t could be int or long
// or long long, so three summary variants would be enough).
// Of course, function variants are also useful for C++ overloads.
+ const QualType VoidTy = ACtx.VoidTy;
const QualType IntTy = ACtx.IntTy;
+ const QualType UnsignedIntTy = ACtx.UnsignedIntTy;
const QualType LongTy = ACtx.LongTy;
const QualType LongLongTy = ACtx.LongLongTy;
const QualType SizeTy = ACtx.getSizeType();
+
const QualType VoidPtrTy = ACtx.VoidPtrTy; // void *
+ const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int *
+ const QualType UnsignedIntPtrTy =
+ ACtx.getPointerType(UnsignedIntTy); // unsigned int *
const QualType VoidPtrRestrictTy =
ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict
: VoidPtrTy;
const QualType ConstVoidPtrTy =
ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void *
+ const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char *
+ const QualType CharPtrRestrictTy =
+ ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict
+ : CharPtrTy;
const QualType ConstCharPtrTy =
ACtx.getPointerType(ACtx.CharTy.withConst()); // const char *
+ const QualType ConstCharPtrRestrictTy =
+ ACtx.getLangOpts().C99
+ ? ACtx.getRestrictType(ConstCharPtrTy) // const char *restrict
+ : ConstCharPtrTy;
+ const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t *
+ const QualType ConstWchar_tPtrTy =
+ ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t *
const QualType ConstVoidPtrRestrictTy =
ACtx.getLangOpts().C99
? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict
: ConstVoidPtrTy;
const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
+ const RangeInt UnsignedIntMax =
+ BVF.getMaxValue(UnsignedIntTy).getLimitedValue();
const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
+ const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue();
// Set UCharRangeMax to min of int or uchar maximum value.
// The C standard states that the arguments of functions like isalpha must
@@ -1110,6 +1136,573 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
{Getline(IntTy, IntMax), Getline(LongTy, LongMax),
Getline(LongLongTy, LongLongMax)});
+ if (ModelPOSIX) {
+
+ // long a64l(const char *str64);
+ addToFunctionSummaryMap(
+ "a64l", Summary(ArgTypes{ConstCharPtrTy}, RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *l64a(long value);
+ addToFunctionSummaryMap(
+ "l64a", Summary(ArgTypes{LongTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, LongMax))));
+
+ // int access(const char *pathname, int amode);
+ addToFunctionSummaryMap("access", Summary(ArgTypes{ConstCharPtrTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int faccessat(int dirfd, const char *pathname, int mode, int flags);
+ addToFunctionSummaryMap(
+ "faccessat", Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int dup(int fildes);
+ addToFunctionSummaryMap(
+ "dup", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // int dup2(int fildes1, int filedes2);
+ addToFunctionSummaryMap(
+ "dup2",
+ Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, IntMax))));
+
+ // int fdatasync(int fildes);
+ addToFunctionSummaryMap(
+ "fdatasync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+
+ // int fnmatch(const char *pattern, const char *string, int flags);
+ addToFunctionSummaryMap(
+ "fnmatch", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fsync(int fildes);
+ addToFunctionSummaryMap(
+ "fsync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ Optional<QualType> Off_tTy = lookupType("off_t", ACtx);
+
+ if (Off_tTy)
+ // int truncate(const char *path, off_t length);
+ addToFunctionSummaryMap("truncate",
+ Summary(ArgTypes{ConstCharPtrTy, *Off_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int symlink(const char *oldpath, const char *newpath);
+ addToFunctionSummaryMap("symlink",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int symlinkat(const char *oldpath, int newdirfd, const char *newpath);
+ addToFunctionSummaryMap(
+ "symlinkat",
+ Summary(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+
+ if (Off_tTy)
+ // int lockf(int fd, int cmd, off_t len);
+ addToFunctionSummaryMap(
+ "lockf",
+ Summary(ArgTypes{IntTy, IntTy, *Off_tTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ Optional<QualType> Mode_tTy = lookupType("mode_t", ACtx);
+
+ if (Mode_tTy)
+ // int creat(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap("creat",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // unsigned int sleep(unsigned int seconds);
+ addToFunctionSummaryMap(
+ "sleep",
+ Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
+
+ Optional<QualType> DirTy = lookupType("DIR", ACtx);
+ Optional<QualType> DirPtrTy;
+ if (DirTy)
+ DirPtrTy = ACtx.getPointerType(*DirTy);
+
+ if (DirPtrTy)
+ // int dirfd(DIR *dirp);
+ addToFunctionSummaryMap(
+ "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // unsigned int alarm(unsigned int seconds);
+ addToFunctionSummaryMap(
+ "alarm",
+ Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
+
+ if (DirPtrTy)
+ // int closedir(DIR *dir);
+ addToFunctionSummaryMap(
+ "closedir", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *strdup(const char *s);
+ addToFunctionSummaryMap("strdup", Summary(ArgTypes{ConstCharPtrTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *strndup(const char *s, size_t n);
+ addToFunctionSummaryMap(
+ "strndup", Summary(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy},
+ NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(1, WithinRange,
+ Range(0, SizeMax))));
+
+ // wchar_t *wcsdup(const wchar_t *s);
+ addToFunctionSummaryMap("wcsdup", Summary(ArgTypes{ConstWchar_tPtrTy},
+ RetType{Wchar_tPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mkstemp(char *template);
+ addToFunctionSummaryMap(
+ "mkstemp", Summary(ArgTypes{CharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *mkdtemp(char *template);
+ addToFunctionSummaryMap(
+ "mkdtemp", Summary(ArgTypes{CharPtrTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *getcwd(char *buf, size_t size);
+ addToFunctionSummaryMap(
+ "getcwd",
+ Summary(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
+
+ if (Mode_tTy) {
+ // int mkdir(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap("mkdir",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mkdirat(int dirfd, const char *pathname, mode_t mode);
+ addToFunctionSummaryMap(
+ "mkdirat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+ }
+
+ Optional<QualType> Dev_tTy = lookupType("dev_t", ACtx);
+
+ if (Mode_tTy && Dev_tTy) {
+ // int mknod(const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap(
+ "mknod", Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy, *Dev_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap("mknodat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
+ *Mode_tTy, *Dev_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+ }
+
+ if (Mode_tTy) {
+ // int chmod(const char *path, mode_t mode);
+ addToFunctionSummaryMap("chmod",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
+ addToFunctionSummaryMap(
+ "fchmodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fchmod(int fildes, mode_t mode);
+ addToFunctionSummaryMap(
+ "fchmod",
+ Summary(ArgTypes{IntTy, *Mode_tTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ }
+
+ Optional<QualType> Uid_tTy = lookupType("uid_t", ACtx);
+ Optional<QualType> Gid_tTy = lookupType("gid_t", ACtx);
+
+ if (Uid_tTy && Gid_tTy) {
+ // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
+ // int flags);
+ addToFunctionSummaryMap(
+ "fchownat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, *Uid_tTy, *Gid_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int chown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "chown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int lchown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "lchown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fchown(int fildes, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "fchown", Summary(ArgTypes{IntTy, *Uid_tTy, *Gid_tTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+ }
+
+ // int rmdir(const char *pathname);
+ addToFunctionSummaryMap(
+ "rmdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int chdir(const char *path);
+ addToFunctionSummaryMap(
+ "chdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int link(const char *oldpath, const char *newpath);
+ addToFunctionSummaryMap("link",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int linkat(int fd1, const char *path1, int fd2, const char *path2,
+ // int flag);
+ addToFunctionSummaryMap(
+ "linkat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // int unlink(const char *pathname);
+ addToFunctionSummaryMap(
+ "unlink", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int unlinkat(int fd, const char *path, int flag);
+ addToFunctionSummaryMap(
+ "unlinkat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructStatTy = lookupType("stat", ACtx);
+ Optional<QualType> StructStatPtrTy, StructStatPtrRestrictTy;
+ if (StructStatTy) {
+ StructStatPtrTy = ACtx.getPointerType(*StructStatTy);
+ StructStatPtrRestrictTy = ACtx.getLangOpts().C99
+ ? ACtx.getRestrictType(*StructStatPtrTy)
+ : *StructStatPtrTy;
+ }
+
+ if (StructStatPtrTy)
+ // int fstat(int fd, struct stat *statbuf);
+ addToFunctionSummaryMap(
+ "fstat",
+ Summary(ArgTypes{IntTy, *StructStatPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ if (StructStatPtrRestrictTy) {
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "stat",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int lstat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "lstat",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fstatat(int fd, const char *restrict path,
+ // struct stat *restrict buf, int flag);
+ addToFunctionSummaryMap(
+ "fstatat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
+ *StructStatPtrRestrictTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+ }
+
+ if (DirPtrTy) {
+ // DIR *opendir(const char *name);
+ addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy},
+ RetType{*DirPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // DIR *fdopendir(int fd);
+ addToFunctionSummaryMap(
+ "fdopendir", Summary(ArgTypes{IntTy}, RetType{*DirPtrTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+ }
+
+ // int isatty(int fildes);
+ addToFunctionSummaryMap(
+ "isatty", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ if (FilePtrTy) {
+ // FILE *popen(const char *command, const char *type);
+ addToFunctionSummaryMap("popen",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{*FilePtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int pclose(FILE *stream);
+ addToFunctionSummaryMap(
+ "pclose", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ // int close(int fildes);
+ addToFunctionSummaryMap(
+ "close", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // long fpathconf(int fildes, int name);
+ addToFunctionSummaryMap(
+ "fpathconf",
+ Summary(ArgTypes{IntTy, IntTy}, RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // long pathconf(const char *path, int name);
+ addToFunctionSummaryMap("pathconf", Summary(ArgTypes{ConstCharPtrTy, IntTy},
+ RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ if (FilePtrTy)
+ // FILE *fdopen(int fd, const char *mode);
+ addToFunctionSummaryMap(
+ "fdopen", Summary(ArgTypes{IntTy, ConstCharPtrTy},
+ RetType{*FilePtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ if (DirPtrTy) {
+ // void rewinddir(DIR *dir);
+ addToFunctionSummaryMap(
+ "rewinddir", Summary(ArgTypes{*DirPtrTy}, RetType{VoidTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void seekdir(DIR *dirp, long loc);
+ addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy},
+ RetType{VoidTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ // int rand_r(unsigned int *seedp);
+ addToFunctionSummaryMap("rand_r", Summary(ArgTypes{UnsignedIntPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int strcasecmp(const char *s1, const char *s2);
+ addToFunctionSummaryMap("strcasecmp",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int strncasecmp(const char *s1, const char *s2, size_t n);
+ addToFunctionSummaryMap(
+ "strncasecmp", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, SizeTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(ArgumentCondition(
+ 2, WithinRange, Range(0, SizeMax))));
+
+ if (FilePtrTy && Off_tTy) {
+
+ // int fileno(FILE *stream);
+ addToFunctionSummaryMap(
+ "fileno", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fseeko(FILE *stream, off_t offset, int whence);
+ addToFunctionSummaryMap("fseeko",
+ Summary(ArgTypes{*FilePtrTy, *Off_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // off_t ftello(FILE *stream);
+ addToFunctionSummaryMap(
+ "ftello", Summary(ArgTypes{*FilePtrTy}, RetType{*Off_tTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ if (Off_tTy) {
+ Optional<RangeInt> Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
+
+ // void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ // off_t offset);
+ addToFunctionSummaryMap(
+ "mmap",
+ Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off_tTy},
+ RetType{VoidPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(0, *Off_tMax))));
+ }
+
+ Optional<QualType> Off64_tTy = lookupType("off64_t", ACtx);
+ Optional<RangeInt> Off64_tMax;
+ if (Off64_tTy) {
+ Off64_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
+ // void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
+ // off64_t offset);
+ addToFunctionSummaryMap(
+ "mmap64",
+ Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off64_tTy},
+ RetType{VoidPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(0, *Off64_tMax))));
+ }
+
+ // int pipe(int fildes[2]);
+ addToFunctionSummaryMap(
+ "pipe", Summary(ArgTypes{IntPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ if (Off_tTy)
+ // off_t lseek(int fildes, off_t offset, int whence);
+ addToFunctionSummaryMap(
+ "lseek", Summary(ArgTypes{IntTy, *Off_tTy, IntTy}, RetType{*Off_tTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+
+ Optional<QualType> Ssize_tTy = lookupType("ssize_t", ACtx);
+
+ if (Ssize_tTy) {
+ // ssize_t readlink(const char *restrict path, char *restrict buf,
+ // size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlink",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
+ RetType{*Ssize_tTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2)))
+ .ArgConstraint(
+ ArgumentCondition(2, WithinRange, Range(0, SizeMax))));
+
+ // ssize_t readlinkat(int fd, const char *restrict path,
+ // char *restrict buf, size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlinkat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
+ CharPtrRestrictTy, SizeTy},
+ RetType{*Ssize_tTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
+ /*BufSize=*/ArgNo(3)))
+ .ArgConstraint(ArgumentCondition(
+ 3, WithinRange, Range(0, SizeMax))));
+ }
+
+ // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char
+ // *newpath);
+ addToFunctionSummaryMap("renameat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
+ IntTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // char *realpath(const char *restrict file_name,
+ // char *restrict resolved_name);
+ addToFunctionSummaryMap(
+ "realpath", Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst());
+
+ // int execv(const char *path, char *const argv[]);
+ addToFunctionSummaryMap("execv",
+ Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int execvp(const char *file, char *const argv[]);
+ addToFunctionSummaryMap("execvp",
+ Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int getopt(int argc, char * const argv[], const char *optstring);
+ addToFunctionSummaryMap(
+ "getopt",
+ Summary(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+ }
+
// Functions for testing.
if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
addToFunctionSummaryMap(
@@ -1151,6 +1744,8 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
Checker->DisplayLoadedSummaries =
mgr.getAnalyzerOptions().getCheckerBooleanOption(
Checker, "DisplayLoadedSummaries");
+ Checker->ModelPOSIX =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
}
bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) {
diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c
index e4035cf755b2..56c749cc45d8 100644
--- a/clang/test/Analysis/analyzer-config.c
+++ b/clang/test/Analysis/analyzer-config.c
@@ -13,6 +13,7 @@
// CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01
// CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = ""
// CHECK-NEXT: apiModeling.StdCLibraryFunctions:DisplayLoadedSummaries = false
+// CHECK-NEXT: apiModeling.StdCLibraryFunctions:ModelPOSIX = false
// CHECK-NEXT: apply-fixits = false
// CHECK-NEXT: avoid-suppressing-null-argument-paths = false
// CHECK-NEXT: c++-allocator-inlining = true
diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c
new file mode 100644
index 000000000000..e6f5b19baa00
--- /dev/null
+++ b/clang/test/Analysis/std-c-library-functions-POSIX.c
@@ -0,0 +1,178 @@
+// RUN: %clang_analyze_cc1 %s \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
+// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true \
+// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:DisplayLoadedSummaries=true \
+// RUN: -analyzer-checker=debug.ExprInspection \
+// RUN: -analyzer-config eagerly-assume=false \
+// RUN: -triple i686-unknown-linux 2>&1 | FileCheck %s
+
+// CHECK: Loaded summary for: long a64l(const char *str64)
+// CHECK: Loaded summary for: char *l64a(long value)
+// CHECK: Loaded summary for: int access(const char *pathname, int amode)
+// CHECK: Loaded summary for: int faccessat(int dirfd, const char *pathname, int mode, int flags)
+// CHECK: Loaded summary for: int dup(int fildes)
+// CHECK: Loaded summary for: int dup2(int fildes1, int filedes2)
+// CHECK: Loaded summary for: int fdatasync(int fildes)
+// CHECK: Loaded summary for: int fnmatch(const char *pattern, const char *string, int flags)
+// CHECK: Loaded summary for: int fsync(int fildes)
+// CHECK: Loaded summary for: int truncate(const char *path, off_t length)
+// CHECK: Loaded summary for: int symlink(const char *oldpath, const char *newpath)
+// CHECK: Loaded summary for: int symlinkat(const char *oldpath, int newdirfd, const char *newpath)
+// CHECK: Loaded summary for: int lockf(int fd, int cmd, off_t len)
+// CHECK: Loaded summary for: int creat(const char *pathname, mode_t mode)
+// CHECK: Loaded summary for: unsigned int sleep(unsigned int seconds)
+// CHECK: Loaded summary for: int dirfd(DIR *dirp)
+// CHECK: Loaded summary for: unsigned int alarm(unsigned int seconds)
+// CHECK: Loaded summary for: int closedir(DIR *dir)
+// CHECK: Loaded summary for: char *strdup(const char *s)
+// CHECK: Loaded summary for: char *strndup(const char *s, size_t n)
+// CHECK: Loaded summary for: int mkstemp(char *template)
+// CHECK: Loaded summary for: char *mkdtemp(char *template)
+// CHECK: Loaded summary for: char *getcwd(char *buf, size_t size)
+// CHECK: Loaded summary for: int mkdir(const char *pathname, mode_t mode)
+// CHECK: Loaded summary for: int mkdirat(int dirfd, const char *pathname, mode_t mode)
+// CHECK: Loaded summary for: int mknod(const char *pathname, mode_t mode, dev_t dev)
+// CHECK: Loaded summary for: int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev)
+// CHECK: Loaded summary for: int chmod(const char *path, mode_t mode)
+// CHECK: Loaded summary for: int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags)
+// CHECK: Loaded summary for: int fchmod(int fildes, mode_t mode)
+// CHECK: Loaded summary for: int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags)
+// CHECK: Loaded summary for: int chown(const char *path, uid_t owner, gid_t group)
+// CHECK: Loaded summary for: int lchown(const char *path, uid_t owner, gid_t group)
+// CHECK: Loaded summary for: int fchown(int fildes, uid_t owner, gid_t group)
+// CHECK: Loaded summary for: int rmdir(const char *pathname)
+// CHECK: Loaded summary for: int chdir(const char *path)
+// CHECK: Loaded summary for: int link(const char *oldpath, const char *newpath)
+// CHECK: Loaded summary for: int linkat(int fd1, const char *path1, int fd2, const char *path2, int flag)
+// CHECK: Loaded summary for: int unlink(const char *pathname)
+// CHECK: Loaded summary for: int unlinkat(int fd, const char *path, int flag)
+// CHECK: Loaded summary for: int fstat(int fd, struct stat *statbuf)
+// CHECK: Loaded summary for: int stat(const char *restrict path, struct stat *restrict buf)
+// CHECK: Loaded summary for: int lstat(const char *restrict path, struct stat *restrict buf)
+// CHECK: Loaded summary for: int fstatat(int fd, const char *restrict path, struct stat *restrict buf, int flag)
+// 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)
+// CHECK: Loaded summary for: FILE *fdopen(int fd, const char *mode)
+// CHECK: Loaded summary for: void rewinddir(DIR *dir)
+// CHECK: Loaded summary for: void seekdir(DIR *dirp, long loc)
+// CHECK: Loaded summary for: int rand_r(unsigned int *seedp)
+// CHECK: Loaded summary for: int strcasecmp(const char *s1, const char *s2)
+// CHECK: Loaded summary for: int strncasecmp(const char *s1, const char *s2, size_t n)
+// CHECK: Loaded summary for: int fileno(FILE *stream)
+// CHECK: Loaded summary for: int fseeko(FILE *stream, off_t offset, int whence)
+// CHECK: Loaded summary for: off_t ftello(FILE *stream)
+// CHECK: Loaded summary for: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
+// CHECK: Loaded summary for: void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset)
+// CHECK: Loaded summary for: int pipe(int fildes[2])
+// CHECK: Loaded summary for: off_t lseek(int fildes, off_t offset, int whence)
+// CHECK: Loaded summary for: ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
+// CHECK: Loaded summary for: ssize_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize)
+// CHECK: Loaded summary for: int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
+// CHECK: Loaded summary for: char *realpath(const char *restrict file_name, char *restrict resolved_name)
+// CHECK: Loaded summary for: int execv(const char *path, char *const argv[])
+// CHECK: Loaded summary for: int execvp(const char *file, char *const argv[])
+// CHECK: Loaded summary for: int getopt(int argc, char *const argv[], const char *optstring)
+
+long a64l(const char *str64);
+char *l64a(long value);
+int access(const char *pathname, int amode);
+int faccessat(int dirfd, const char *pathname, int mode, int flags);
+int dup(int fildes);
+int dup2(int fildes1, int filedes2);
+int fdatasync(int fildes);
+int fnmatch(const char *pattern, const char *string, int flags);
+int fsync(int fildes);
+typedef unsigned long off_t;
+int truncate(const char *path, off_t length);
+int symlink(const char *oldpath, const char *newpath);
+int symlinkat(const char *oldpath, int newdirfd, const char *newpath);
+int lockf(int fd, int cmd, off_t len);
+typedef unsigned mode_t;
+int creat(const char *pathname, mode_t mode);
+unsigned int sleep(unsigned int seconds);
+typedef struct {
+ int a;
+} DIR;
+int dirfd(DIR *dirp);
+unsigned int alarm(unsigned int seconds);
+int closedir(DIR *dir);
+char *strdup(const char *s);
+typedef typeof(sizeof(int)) size_t;
+char *strndup(const char *s, size_t n);
+/*FIXME How to define wchar_t in the test?*/
+/*typedef __wchar_t wchar_t;*/
+/*wchar_t *wcsdup(const wchar_t *s);*/
+int mkstemp(char *template);
+char *mkdtemp(char *template);
+char *getcwd(char *buf, size_t size);
+int mkdir(const char *pathname, mode_t mode);
+int mkdirat(int dirfd, const char *pathname, mode_t mode);
+typedef int dev_t;
+int mknod(const char *pathname, mode_t mode, dev_t dev);
+int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
+int chmod(const char *path, mode_t mode);
+int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
+int fchmod(int fildes, mode_t mode);
+typedef int uid_t;
+typedef int gid_t;
+int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);
+int chown(const char *path, uid_t owner, gid_t group);
+int lchown(const char *path, uid_t owner, gid_t group);
+int fchown(int fildes, uid_t owner, gid_t group);
+int rmdir(const char *pathname);
+int chdir(const char *path);
+int link(const char *oldpath, const char *newpath);
+int linkat(int fd1, const char *path1, int fd2, const char *path2, int flag);
+int unlink(const char *pathname);
+int unlinkat(int fd, const char *path, int flag);
+struct stat;
+int fstat(int fd, struct stat *statbuf);
+int stat(const char *restrict path, struct stat *restrict buf);
+int lstat(const char *restrict path, struct stat *restrict buf);
+int fstatat(int fd, const char *restrict path, struct stat *restrict buf, int flag);
+DIR *opendir(const char *name);
+DIR *fdopendir(int fd);
+int isatty(int fildes);
+typedef struct {
+ int x;
+} FILE;
+FILE *popen(const char *command, const char *type);
+int pclose(FILE *stream);
+int close(int fildes);
+long fpathconf(int fildes, int name);
+long pathconf(const char *path, int name);
+FILE *fdopen(int fd, const char *mode);
+void rewinddir(DIR *dir);
+void seekdir(DIR *dirp, long loc);
+int rand_r(unsigned int *seedp);
+int strcasecmp(const char *s1, const char *s2);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+int fileno(FILE *stream);
+int fseeko(FILE *stream, off_t offset, int whence);
+off_t ftello(FILE *stream);
+void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
+typedef off_t off64_t;
+void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
+int pipe(int fildes[2]);
+off_t lseek(int fildes, off_t offset, int whence);
+typedef size_t ssize_t;
+ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize);
+ssize_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);
+int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+char *realpath(const char *restrict file_name, char *restrict resolved_name);
+int execv(const char *path, char *const argv[]);
+int execvp(const char *file, char *const argv[]);
+int getopt(int argc, char *const argv[], const char *optstring);
+
+// Must have at least one call expression to initialize the summary map.
+int bar(void);
+void foo() {
+ bar();
+}
More information about the cfe-commits
mailing list