diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -354,6 +354,12 @@ "If set to true, the checker displays the found summaries " "for the given translation unit.", "false", + Released>, + CmdLineOption ]>, Documentation; diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -461,6 +461,7 @@ CheckerNameRef CheckNames[CK_NumCheckKinds]; bool DisplayLoadedSummaries = false; + bool ModelPOSIX = false; private: Optional findFunctionSummary(const FunctionDecl *FD, @@ -736,13 +737,18 @@ // 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(D)) return ACtx.getTypeDeclType(TD).getCanonicalType(); - } - assert(LookupRes.size() == 1 && "Type identifier should be unique"); - auto *D = cast(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(D)) + return ACtx.getTypeDeclType(TD).getCanonicalType(); + return None; } void StdLibraryFunctionsChecker::initFunctionSummaries( @@ -761,26 +767,46 @@ // 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,574 @@ {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 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 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 DirTy = lookupType("DIR", ACtx); + Optional 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 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 Uid_tTy = lookupType("uid_t", ACtx); + Optional 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 StructStatTy = lookupType("stat", ACtx); + Optional 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)))); + } + + Optional Off_tMax; + if (Off_tTy) { + 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 Off64_tTy = lookupType("off64_t", ACtx); + Optional 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 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 +1745,8 @@ 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 --- 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 --- /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(); +}