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 @@ -302,6 +302,13 @@ Tmp.Op = BinaryOperator::negateComparisonOp(Op); return std::make_shared(Tmp); } + + bool checkSpecificValidity(const FunctionDecl *FD) const override { + const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); + assert(ValidArg && + "This constraint should be applied only on a pointer type"); + return ValidArg; + } }; /// The complete list of constraints that defines a single branch. @@ -839,11 +846,13 @@ // Add a summary to a FunctionDecl found by lookup. The lookup is performed // by the given Name, and in the global scope. The summary will be attached // to the found FunctionDecl only if the signatures match. - void operator()(StringRef Name, Summary S) { + // + // Returns true if the summary has been added, false otherwise. + bool operator()(StringRef Name, Summary S) { IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.size() == 0) - return; + return false; for (Decl *D : LookupRes) { if (auto *FD = dyn_cast(D)) { if (S.matchesAndSet(FD)) { @@ -855,10 +864,11 @@ FD->print(llvm::errs()); llvm::errs() << "\n"; } - return; + return true; } } } + return false; } // Add several summaries for the given name. void operator()(StringRef Name, const std::vector &Summaries) { @@ -1701,6 +1711,279 @@ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); + + Optional StructSockaddrTy = lookupType("sockaddr", ACtx); + Optional StructSockaddrPtrTy, ConstStructSockaddrPtrTy, + StructSockaddrPtrRestrictTy, ConstStructSockaddrPtrRestrictTy; + if (StructSockaddrTy) { + StructSockaddrPtrTy = ACtx.getPointerType(*StructSockaddrTy); + ConstStructSockaddrPtrTy = + ACtx.getPointerType(StructSockaddrTy->withConst()); + StructSockaddrPtrRestrictTy = + ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*StructSockaddrPtrTy) + : *StructSockaddrPtrTy; + ConstStructSockaddrPtrRestrictTy = + ACtx.getLangOpts().C99 + ? ACtx.getRestrictType(*ConstStructSockaddrPtrTy) + : *ConstStructSockaddrPtrTy; + } + Optional Socklen_tTy = lookupType("socklen_t", ACtx); + Optional Socklen_tPtrTy, Socklen_tPtrRestrictTy; + Optional Socklen_tMax; + if (Socklen_tTy) { + Socklen_tMax = BVF.getMaxValue(*Socklen_tTy).getLimitedValue(); + Socklen_tPtrTy = ACtx.getPointerType(*Socklen_tTy); + Socklen_tPtrRestrictTy = ACtx.getLangOpts().C99 + ? ACtx.getRestrictType(*Socklen_tPtrTy) + : *Socklen_tPtrTy; + } + + // In 'socket.h' of some libc implementations with C99, sockaddr parameter + // is a transparent union of the underlying sockaddr_ family of pointers + // instead of being a pointer to struct sockaddr. In these cases, the + // standardized signature will not match, thus we try to match with another + // signature that has the joker Irrelevant type. We also remove those + // constraints which require pointer types for the sockaddr param. + if (StructSockaddrPtrRestrictTy && Socklen_tPtrRestrictTy) { + // int accept(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (!addToFunctionSummaryMap( + "accept", Summary(ArgTypes{IntTy, *StructSockaddrPtrRestrictTy, + *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax))))) + // Do not add constraints on sockaddr. + addToFunctionSummaryMap( + "accept", + Summary(ArgTypes{IntTy, Irrelevant, *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // int bind(int socket, const struct sockaddr *address, socklen_t + // address_len); + if (!addToFunctionSummaryMap( + "bind", + Summary(ArgTypes{IntTy, *ConstStructSockaddrPtrTy, *Socklen_tTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))) + .ArgConstraint(ArgumentCondition(2, WithinRange, + Range(0, *Socklen_tMax))))) + addToFunctionSummaryMap( + "bind", Summary(ArgTypes{IntTy, Irrelevant, *Socklen_tTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(ArgumentCondition( + 2, WithinRange, Range(0, *Socklen_tMax)))); + + // int getpeername(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (!addToFunctionSummaryMap( + "getpeername", + Summary(ArgTypes{IntTy, *StructSockaddrPtrRestrictTy, + *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2))))) + addToFunctionSummaryMap( + "getpeername", + Summary(ArgTypes{IntTy, Irrelevant, *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // int getsockname(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (!addToFunctionSummaryMap( + "getsockname", + Summary(ArgTypes{IntTy, *StructSockaddrPtrRestrictTy, + *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2))))) + addToFunctionSummaryMap( + "getsockname", + Summary(ArgTypes{IntTy, Irrelevant, *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // int connect(int socket, const struct sockaddr *address, socklen_t + // address_len); + if (!addToFunctionSummaryMap( + "connect", + Summary(ArgTypes{IntTy, *ConstStructSockaddrPtrTy, *Socklen_tTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))))) + addToFunctionSummaryMap( + "connect", Summary(ArgTypes{IntTy, Irrelevant, *Socklen_tTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, + Range(0, IntMax)))); + + // ssize_t recvfrom(int socket, void *restrict buffer, size_t length, + // int flags, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (Ssize_tTy && + !addToFunctionSummaryMap( + "recvfrom", Summary(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, + IntTy, *StructSockaddrPtrRestrictTy, + *Socklen_tPtrRestrictTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))))) + addToFunctionSummaryMap( + "recvfrom", + Summary(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, + Irrelevant, *Socklen_tPtrRestrictTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); + + // ssize_t sendto(int socket, const void *message, size_t length, + // int flags, const struct sockaddr *dest_addr, + // socklen_t dest_len); + if (Ssize_tTy && + !addToFunctionSummaryMap( + "sendto", + Summary(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, + *ConstStructSockaddrPtrTy, *Socklen_tTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))))) + addToFunctionSummaryMap( + "sendto", Summary(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, + Irrelevant, *Socklen_tTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, + Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); + } + + // int listen(int sockfd, int backlog); + addToFunctionSummaryMap( + "listen", Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + if (Ssize_tTy) + // ssize_t recv(int sockfd, void *buf, size_t len, int flags); + addToFunctionSummaryMap( + "recv", Summary(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); + + Optional StructMsghdrTy = lookupType("msghdr", ACtx); + Optional StructMsghdrPtrTy, ConstStructMsghdrPtrTy; + if (StructMsghdrTy) { + StructMsghdrPtrTy = ACtx.getPointerType(*StructMsghdrTy); + ConstStructMsghdrPtrTy = ACtx.getPointerType(StructMsghdrTy->withConst()); + } + + if (Ssize_tTy && StructMsghdrPtrTy) + // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); + addToFunctionSummaryMap( + "recvmsg", Summary(ArgTypes{IntTy, *StructMsghdrPtrTy, IntTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, + Range(0, IntMax)))); + + if (Ssize_tTy && ConstStructMsghdrPtrTy) + // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); + addToFunctionSummaryMap( + "sendmsg", Summary(ArgTypes{IntTy, *ConstStructMsghdrPtrTy, IntTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, + Range(0, IntMax)))); + + if (Socklen_tTy) + // int setsockopt(int socket, int level, int option_name, + // const void *option_value, socklen_t option_len); + addToFunctionSummaryMap( + "setsockopt", + Summary(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, *Socklen_tTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) + .ArgConstraint( + ArgumentCondition(4, WithinRange, Range(0, *Socklen_tMax)))); + + if (Socklen_tPtrRestrictTy) + // int getsockopt(int socket, int level, int option_name, + // void *restrict option_value, + // socklen_t *restrict option_len); + addToFunctionSummaryMap( + "getsockopt", Summary(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy, + *Socklen_tPtrRestrictTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint(NotNull(ArgNo(4)))); + + if (Ssize_tTy) + // ssize_t send(int sockfd, const void *buf, size_t len, int flags); + addToFunctionSummaryMap( + "send", Summary(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy}, + RetType{*Ssize_tTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); + + // int socketpair(int domain, int type, int protocol, int sv[2]); + addToFunctionSummaryMap("socketpair", + Summary(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(3)))); + + if (ConstStructSockaddrPtrRestrictTy && Socklen_tTy) + // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, + // char *restrict node, socklen_t nodelen, + // char *restrict service, + // socklen_t servicelen, int flags); + // + // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr + // parameter is never handled as a transparent union in netdb.h + addToFunctionSummaryMap( + "getnameinfo", + Summary(ArgTypes{*ConstStructSockaddrPtrRestrictTy, *Socklen_tTy, + CharPtrRestrictTy, *Socklen_tTy, CharPtrRestrictTy, + *Socklen_tTy, IntTy}, + RetType{IntTy}, NoEvalCall) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) + .ArgConstraint( + ArgumentCondition(1, WithinRange, Range(0, *Socklen_tMax))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3))) + .ArgConstraint( + ArgumentCondition(3, WithinRange, Range(0, *Socklen_tMax))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) + .ArgConstraint( + ArgumentCondition(5, WithinRange, Range(0, *Socklen_tMax)))); } // Functions for testing. diff --git a/clang/test/Analysis/std-c-library-functions-POSIX-socket-sockaddr.cpp b/clang/test/Analysis/std-c-library-functions-POSIX-socket-sockaddr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/std-c-library-functions-POSIX-socket-sockaddr.cpp @@ -0,0 +1,37 @@ +// 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 + +// We test here that functions from socket.h are added when sockaddr is not a +// transparent union of other sockaddr_ pointers. This is the case in C++. + +// CHECK: Loaded summary for: int accept(int socket, struct sockaddr *address, socklen_t *address_len) +// CHECK: Loaded summary for: int bind(int socket, const struct sockaddr *address, socklen_t address_len) +// CHECK: Loaded summary for: int getpeername(int socket, struct sockaddr *address, socklen_t *address_len) +// CHECK: Loaded summary for: int getsockname(int socket, struct sockaddr *address, socklen_t *address_len) +// CHECK: Loaded summary for: int connect(int socket, const struct sockaddr *address, socklen_t address_len) +// CHECK: Loaded summary for: ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len) +// CHECK: Loaded summary for: ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) + +struct sockaddr; +using socklen_t = unsigned; +int accept(int socket, struct sockaddr *address, socklen_t *address_len); +int bind(int socket, const struct sockaddr *address, socklen_t address_len); +int getpeername(int socket, struct sockaddr *address, socklen_t *address_len); +int getsockname(int socket, struct sockaddr *address, socklen_t *address_len); +int connect(int socket, const struct sockaddr *address, socklen_t address_len); +typedef decltype(sizeof(int)) size_t; +typedef size_t ssize_t; +ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len); +ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len); + +// Must have at least one call expression to initialize the summary map. +int bar(void); +void foo() { + bar(); +} 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 @@ -79,6 +79,22 @@ // 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) +// CHECK: Loaded summary for: int accept(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len) +// CHECK: Loaded summary for: int bind(int socket, __CONST_SOCKADDR_ARG address, socklen_t address_len) +// CHECK: Loaded summary for: int getpeername(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len) +// CHECK: Loaded summary for: int getsockname(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len) +// CHECK: Loaded summary for: int connect(int socket, __CONST_SOCKADDR_ARG address, socklen_t address_len) +// CHECK: Loaded summary for: ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, __SOCKADDR_ARG address, socklen_t *restrict address_len) +// CHECK: Loaded summary for: ssize_t sendto(int socket, const void *message, size_t length, int flags, __CONST_SOCKADDR_ARG dest_addr, socklen_t dest_len) +// CHECK: Loaded summary for: int listen(int sockfd, int backlog) +// CHECK: Loaded summary for: ssize_t recv(int sockfd, void *buf, size_t len, int flags) +// CHECK: Loaded summary for: ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) +// CHECK: Loaded summary for: ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) +// CHECK: Loaded summary for: int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len) +// CHECK: Loaded summary for: int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len) +// 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) long a64l(const char *str64); char *l64a(long value); @@ -171,6 +187,47 @@ int execvp(const char *file, char *const argv[]); int getopt(int argc, char *const argv[], const char *optstring); +// In some libc implementations, sockaddr parameter is a transparent +// union of the underlying sockaddr_ pointers instead of being a +// pointer to struct sockaddr. +// We match that with the joker Irrelevant type. +struct sockaddr; +struct sockaddr_at; +#define __SOCKADDR_ALLTYPES \ + __SOCKADDR_ONETYPE(sockaddr) \ + __SOCKADDR_ONETYPE(sockaddr_at) +#define __SOCKADDR_ONETYPE(type) struct type *__restrict __##type##__; +#define __SOCKADDR_ONETYPE(type) struct type *__restrict __##type##__; +typedef union { + __SOCKADDR_ALLTYPES +} __SOCKADDR_ARG __attribute__((__transparent_union__)); +#undef __SOCKADDR_ONETYPE +#define __SOCKADDR_ONETYPE(type) const struct type *__restrict __##type##__; +typedef union { + __SOCKADDR_ALLTYPES +} __CONST_SOCKADDR_ARG __attribute__((__transparent_union__)); +#undef __SOCKADDR_ONETYPE +typedef unsigned socklen_t; + +int accept(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len); +int bind(int socket, __CONST_SOCKADDR_ARG address, socklen_t address_len); +int getpeername(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len); +int getsockname(int socket, __SOCKADDR_ARG address, socklen_t *restrict address_len); +int connect(int socket, __CONST_SOCKADDR_ARG address, socklen_t address_len); +ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, __SOCKADDR_ARG address, socklen_t *restrict address_len); +ssize_t sendto(int socket, const void *message, size_t length, int flags, __CONST_SOCKADDR_ARG dest_addr, socklen_t dest_len); + +int listen(int sockfd, int backlog); +ssize_t recv(int sockfd, void *buf, size_t len, int flags); +struct msghdr; +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); +int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len); +int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len); +ssize_t send(int sockfd, const void *buf, size_t len, int flags); +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); + // Must have at least one call expression to initialize the summary map. int bar(void); void foo() {