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 @@ -314,7 +314,7 @@ /// The complete list of constraints that defines a single branch. typedef std::vector ConstraintSet; - using ArgTypes = std::vector; + using ArgTypes = std::vector>; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -324,17 +324,36 @@ // The signature of a function we want to describe with a summary. This is a // concessive signature, meaning there may be irrelevant types in the // signature which we do not check against a function with concrete types. - struct Signature { - ArgTypes ArgTys; + class Signature { + using ArgQualTypes = std::vector; + ArgQualTypes ArgTys; QualType RetTy; - Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) { - assertRetTypeSuitableForSignature(RetTy); - for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { - QualType ArgTy = ArgTys[I]; - assertArgTypeSuitableForSignature(ArgTy); + // True if any component type is not found by lookup. + bool Invalid = false; + + public: + // Construct a signature from optional types. If any of the optional types + // are not set then the signature will be invalid. + Signature(ArgTypes ArgTys, Optional RetTy) { + for (Optional Arg : ArgTys) { + if (!Arg) { + Invalid = true; + return; + } else { + assertArgTypeSuitableForSignature(*Arg); + this->ArgTys.push_back(*Arg); + } + } + if (!RetTy) { + Invalid = true; + return; + } else { + assertRetTypeSuitableForSignature(*RetTy); + this->RetTy = *RetTy; } } + bool isInvalid() const { return Invalid; } bool matches(const FunctionDecl *FD) const; private: @@ -388,6 +407,9 @@ /// rules for the given parameter's type, those rules are checked once the /// signature is matched. class Summary { + // FIXME Probably the Signature should not be part of the Summary, + // We can remove once all overload of addToFunctionSummaryMap requires the + // Signature explicitly given. Optional Sign; const InvalidationKind InvalidationKd; Cases CaseConstraints; @@ -403,6 +425,8 @@ Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} + // FIXME Remove, once all overload of addToFunctionSummaryMap requires the + // Signature explicitly given. Summary &setSignature(const Signature &S) { Sign = S; return *this; @@ -438,6 +462,13 @@ return Result; } + // FIXME Remove, once all overload of addToFunctionSummaryMap requires the + // Signature explicitly given. + bool hasInvalidSignature() { + assert(Sign && "Signature must be set before this query"); + return Sign->isInvalid(); + } + private: // Once we know the exact type of the function then do sanity check on all // the given constraints. @@ -699,6 +730,7 @@ bool StdLibraryFunctionsChecker::Signature::matches( const FunctionDecl *FD) const { + assert(!isInvalid()); // Check number of arguments: if (FD->param_size() != ArgTys.size()) return false; @@ -778,9 +810,38 @@ BasicValueFactory &BVF = SVB.getBasicValueFactory(); const ASTContext &ACtx = BVF.getContext(); - auto getRestrictTy = [&ACtx](QualType Ty) { - return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; - }; + class GetRestrictTy { + const ASTContext &ACtx; + + public: + GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} + QualType operator()(QualType Ty) { + return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; + } + Optional operator()(Optional Ty) { + if (Ty) + return operator()(*Ty); + return None; + } + } getRestrictTy(ACtx); + + class GetPointerTy { + const ASTContext &ACtx; + + public: + GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} + QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } + Optional operator()(Optional Ty) { + if (Ty) + return operator()(*Ty); + return None; + } + Optional withConst(Optional Ty) { + if (Ty) + return operator()(Ty->withConst()); + return None; + } + } getPointerTy(ACtx); // These types are useful for writing specifications quickly, // New specifications should probably introduce more types. @@ -797,21 +858,23 @@ const QualType SizeTy = ACtx.getSizeType(); const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * - const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int * + const QualType IntPtrTy = getPointerTy(IntTy); // int * const QualType UnsignedIntPtrTy = - ACtx.getPointerType(UnsignedIntTy); // unsigned int * + getPointerTy(UnsignedIntTy); // unsigned int * const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); const QualType ConstVoidPtrTy = - ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * - const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char * + getPointerTy(ACtx.VoidTy.withConst()); // const void * + const QualType CharPtrTy = getPointerTy(ACtx.CharTy); // char * const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); const QualType ConstCharPtrTy = - ACtx.getPointerType(ACtx.CharTy.withConst()); // const char * + getPointerTy(ACtx.CharTy.withConst()); // const char * const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); - const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t * + const QualType Wchar_tPtrTy = getPointerTy(ACtx.WCharTy); // wchar_t * const QualType ConstWchar_tPtrTy = - ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t * + getPointerTy(ACtx.WCharTy.withConst()); // const wchar_t * const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); + const QualType SizePtrTy = getPointerTy(SizeTy); + const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); const RangeInt UnsignedIntMax = @@ -853,7 +916,10 @@ // to the found FunctionDecl only if the signatures match. // // Returns true if the summary has been added, false otherwise. + // FIXME remove all overloads without the explicit Signature parameter. bool operator()(StringRef Name, Summary S) { + if (S.hasInvalidSignature()) + return false; IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.size() == 0) @@ -875,6 +941,7 @@ } return false; } + // Add the summary with the Signature explicitly given. bool operator()(StringRef Name, Signature Sign, Summary Sum) { return operator()(Name, Sum.setSignature(Sign)); } @@ -883,6 +950,17 @@ for (const Summary &S : Summaries) operator()(Name, S); } + // Add the same summary for different names. + void operator()(std::vector Names, Summary S) { + for (StringRef Name : Names) + operator()(Name, S); + } + // Add the same summary for different names with the Signature explicitly + // given. + void operator()(std::vector Names, Signature Sign, Summary Sum) { + for (StringRef Name : Names) + operator()(Name, Sign, Sum); + } } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); // We are finally ready to define specifications for all supported functions. @@ -942,7 +1020,7 @@ Optional FilePtrTy, FilePtrRestrictTy; if (FileTy) { // FILE * - FilePtrTy = ACtx.getPointerType(*FileTy); + FilePtrTy = getPointerTy(*FileTy); // FILE *restrict FilePtrRestrictTy = getRestrictTy(*FilePtrTy); } @@ -1262,7 +1340,7 @@ Optional DirTy = lookupType("DIR", ACtx); Optional DirPtrTy; if (DirTy) - DirPtrTy = ACtx.getPointerType(*DirTy); + DirPtrTy = getPointerTy(*DirTy); if (DirPtrTy) // int dirfd(DIR *dirp); @@ -1449,7 +1527,7 @@ Optional StructStatTy = lookupType("stat", ACtx); Optional StructStatPtrTy, StructStatPtrRestrictTy; if (StructStatTy) { - StructStatPtrTy = ACtx.getPointerType(*StructStatTy); + StructStatPtrTy = getPointerTy(*StructStatTy); StructStatPtrRestrictTy = getRestrictTy(*StructStatPtrTy); } @@ -1694,7 +1772,7 @@ RetType{CharPtrTy}, NoEvalCall) .ArgConstraint(NotNull(ArgNo(0)))); - QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst()); + QualType CharPtrConstPtr = getPointerTy(CharPtrTy.withConst()); // int execv(const char *path, char *const argv[]); addToFunctionSummaryMap("execv", @@ -1721,9 +1799,8 @@ Optional StructSockaddrPtrTy, ConstStructSockaddrPtrTy, StructSockaddrPtrRestrictTy, ConstStructSockaddrPtrRestrictTy; if (StructSockaddrTy) { - StructSockaddrPtrTy = ACtx.getPointerType(*StructSockaddrTy); - ConstStructSockaddrPtrTy = - ACtx.getPointerType(StructSockaddrTy->withConst()); + StructSockaddrPtrTy = getPointerTy(*StructSockaddrTy); + ConstStructSockaddrPtrTy = getPointerTy(StructSockaddrTy->withConst()); StructSockaddrPtrRestrictTy = getRestrictTy(*StructSockaddrPtrTy); ConstStructSockaddrPtrRestrictTy = getRestrictTy(*ConstStructSockaddrPtrTy); @@ -1733,7 +1810,7 @@ Optional Socklen_tMax; if (Socklen_tTy) { Socklen_tMax = BVF.getMaxValue(*Socklen_tTy).getLimitedValue(); - Socklen_tPtrTy = ACtx.getPointerType(*Socklen_tTy); + Socklen_tPtrTy = getPointerTy(*Socklen_tTy); Socklen_tPtrRestrictTy = getRestrictTy(*Socklen_tPtrTy); } @@ -1900,8 +1977,8 @@ Optional StructMsghdrTy = lookupType("msghdr", ACtx); Optional StructMsghdrPtrTy, ConstStructMsghdrPtrTy; if (StructMsghdrTy) { - StructMsghdrPtrTy = ACtx.getPointerType(*StructMsghdrTy); - ConstStructMsghdrPtrTy = ACtx.getPointerType(StructMsghdrTy->withConst()); + StructMsghdrPtrTy = getPointerTy(*StructMsghdrTy); + ConstStructMsghdrPtrTy = getPointerTy(StructMsghdrTy->withConst()); } if (Ssize_tTy && StructMsghdrPtrTy) @@ -1986,6 +2063,100 @@ BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) .ArgConstraint( ArgumentCondition(5, WithinRange, Range(0, *Socklen_tMax)))); + + Optional Pthread_cond_tTy = lookupType("pthread_cond_t", ACtx); + Optional Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy); + Optional Pthread_tTy = lookupType("pthread_t", ACtx); + Optional Pthread_tPtrTy = getPointerTy(Pthread_tTy); + Optional Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy); + Optional Pthread_mutex_tTy = lookupType("pthread_mutex_t", ACtx); + Optional Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy); + Optional Pthread_mutex_tPtrRestrictTy = + getRestrictTy(Pthread_mutex_tPtrTy); + Optional Pthread_attr_tTy = lookupType("pthread_attr_t", ACtx); + Optional Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy); + Optional ConstPthread_attr_tPtrTy = + getPointerTy.withConst(Pthread_attr_tTy); + Optional ConstPthread_attr_tPtrRestrictTy = + getRestrictTy(ConstPthread_attr_tPtrTy); + Optional Pthread_mutexattr_tTy = + lookupType("pthread_mutexattr_t", ACtx); + Optional ConstPthread_mutexattr_tPtrTy = + getPointerTy.withConst(Pthread_mutexattr_tTy); + Optional ConstPthread_mutexattr_tPtrRestrictTy = + getRestrictTy(ConstPthread_mutexattr_tPtrTy); + + QualType PthreadStartRoutineTy = getPointerTy( + ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, + FunctionProtoType::ExtProtoInfo())); + + // int pthread_cond_signal(pthread_cond_t *cond); + // int pthread_cond_broadcast(pthread_cond_t *cond); + addToFunctionSummaryMap( + {"pthread_cond_signal", "pthread_cond_broadcast"}, + Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_create(pthread_t *restrict thread, + // const pthread_attr_t *restrict attr, + // void *(*start_routine)(void*), void *restrict arg); + addToFunctionSummaryMap( + "pthread_create", + Signature(ArgTypes{Pthread_tPtrRestrictTy, + ConstPthread_attr_tPtrRestrictTy, + PthreadStartRoutineTy, VoidPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(2)))); + + // int pthread_attr_destroy(pthread_attr_t *attr); + // int pthread_attr_init(pthread_attr_t *attr); + addToFunctionSummaryMap( + {"pthread_attr_destroy", "pthread_attr_init"}, + Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, + // size_t *restrict stacksize); + // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, + // size_t *restrict guardsize); + addToFunctionSummaryMap( + {"pthread_attr_getstacksize", "pthread_attr_getguardsize"}, + Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); + // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); + addToFunctionSummaryMap( + {"pthread_attr_setstacksize", "pthread_attr_setguardsize"}, + Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint( + ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); + + // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const + // pthread_mutexattr_t *restrict attr); + addToFunctionSummaryMap( + "pthread_mutex_init", + Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy, + ConstPthread_mutexattr_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_mutex_destroy(pthread_mutex_t *mutex); + // int pthread_mutex_lock(pthread_mutex_t *mutex); + // int pthread_mutex_trylock(pthread_mutex_t *mutex); + // int pthread_mutex_unlock(pthread_mutex_t *mutex); + addToFunctionSummaryMap( + {"pthread_mutex_destroy", "pthread_mutex_lock", "pthread_mutex_trylock", + "pthread_mutex_unlock"}, + Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); } // Functions for testing. diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c --- a/clang/test/Analysis/std-c-library-functions-POSIX.c +++ b/clang/test/Analysis/std-c-library-functions-POSIX.c @@ -95,6 +95,20 @@ // CHECK: Loaded summary for: ssize_t send(int sockfd, const void *buf, size_t len, int flags) // CHECK: Loaded summary for: int socketpair(int domain, int type, int protocol, int sv[2]) // CHECK: Loaded summary for: int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, char *restrict node, socklen_t nodelen, char *restrict service, socklen_t servicelen, int flags) +// CHECK: Loaded summary for: int pthread_cond_signal(pthread_cond_t *cond) +// CHECK: Loaded summary for: int pthread_cond_broadcast(pthread_cond_t *cond) +// CHECK: Loaded summary for: int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg) +// CHECK: Loaded summary for: int pthread_attr_destroy(pthread_attr_t *attr) +// CHECK: Loaded summary for: int pthread_attr_init(pthread_attr_t *attr) +// CHECK: Loaded summary for: int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize) +// CHECK: Loaded summary for: int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize) +// CHECK: Loaded summary for: int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +// CHECK: Loaded summary for: int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +// CHECK: Loaded summary for: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr) +// CHECK: Loaded summary for: int pthread_mutex_destroy(pthread_mutex_t *mutex) +// CHECK: Loaded summary for: int pthread_mutex_lock(pthread_mutex_t *mutex) +// CHECK: Loaded summary for: int pthread_mutex_trylock(pthread_mutex_t *mutex) +// CHECK: Loaded summary for: int pthread_mutex_unlock(pthread_mutex_t *mutex) long a64l(const char *str64); char *l64a(long value); @@ -227,6 +241,26 @@ int socketpair(int domain, int type, int protocol, int sv[2]); int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, char *restrict node, socklen_t nodelen, char *restrict service, socklen_t servicelen, int flags); +typedef union { int x; } pthread_cond_t; +int pthread_cond_signal(pthread_cond_t *cond); +int pthread_cond_broadcast(pthread_cond_t *cond); +typedef union { int x; } pthread_attr_t; +typedef unsigned long int pthread_t; +int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg); +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize); +int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); +typedef union { int x; } pthread_mutex_t; +typedef union { int x; } pthread_mutexattr_t; +int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); +int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); + // Must have at least one call expression to initialize the summary map. int bar(void); void foo() {