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,12 +302,20 @@ 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. typedef std::vector ConstraintSet; - using ArgTypes = std::vector; + using ArgTypes = std::vector>; + using RetType = Optional; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -317,16 +325,37 @@ // 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 { - const ArgTypes ArgTys; - const 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); + // All types in the spec need to be canonical. + class Signature { + using ArgQualTypes = std::vector; + ArgQualTypes ArgTys; + QualType RetTy; + // 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, RetType 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: @@ -380,7 +409,10 @@ /// rules for the given parameter's type, those rules are checked once the /// signature is matched. class Summary { - const Signature Sign; + // 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; ConstraintSet ArgConstraints; @@ -390,8 +422,17 @@ const FunctionDecl *FD = nullptr; public: - Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd) - : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {} + Summary(ArgTypes ArgTys, RetType RetTy, InvalidationKind InvalidationKd) + : Sign(Signature(ArgTys, RetTy)), InvalidationKd(InvalidationKd) {} + + 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; + } Summary &Case(ConstraintSet&& CS) { CaseConstraints.push_back(std::move(CS)); @@ -413,7 +454,9 @@ // Returns true if the summary should be applied to the given function. // And if yes then store the function declaration. bool matchesAndSet(const FunctionDecl *FD) { - bool Result = Sign.matches(FD) && validateByConstraints(FD); + assert(Sign && + "Signature must be set before comparing to a FunctionDecl"); + bool Result = Sign->matches(FD) && validateByConstraints(FD); if (Result) { assert(!this->FD && "FD must not be set more than once"); this->FD = FD; @@ -421,6 +464,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. @@ -682,6 +732,7 @@ bool StdLibraryFunctionsChecker::Signature::matches( const FunctionDecl *FD) const { + assert(!isInvalid()); // Check number of arguments: if (FD->param_size() != ArgTys.size()) return false; @@ -726,32 +777,6 @@ return findFunctionSummary(FD, C); } -static llvm::Optional lookupType(StringRef Name, - const ASTContext &ACtx) { - IdentifierInfo &II = ACtx.Idents.get(Name); - auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); - if (LookupRes.size() == 0) - return None; - - // Prioritze typedef declarations. - // This is needed in case of C struct typedefs. E.g.: - // 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) - if (auto *TD = dyn_cast(D)) - return ACtx.getTypeDeclType(TD).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( CheckerContext &C) const { if (!FunctionSummaryMap.empty()) @@ -761,6 +786,91 @@ BasicValueFactory &BVF = SVB.getBasicValueFactory(); const ASTContext &ACtx = BVF.getContext(); + // Helper class to lookup a type by its name. + class LookupType { + const ASTContext &ACtx; + + public: + LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} + + // Find the type. If not found then the optional is not set. + llvm::Optional operator()(StringRef Name) { + IdentifierInfo &II = ACtx.Idents.get(Name); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + if (LookupRes.size() == 0) + return None; + + // Prioritze typedef declarations. + // This is needed in case of C struct typedefs. E.g.: + // 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) + if (auto *TD = dyn_cast(D)) + return ACtx.getTypeDeclType(TD).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; + } + } lookupTy(ACtx); + + // Below are auxiliary classes to handle optional types that we get as a + // result of the lookup. + 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; + } + } getPointerTy(ACtx); + class { + public: + Optional operator()(Optional Ty) { + return Ty ? Optional(Ty->withConst()) : None; + } + QualType operator()(QualType Ty) { return Ty.withConst(); } + } getConstTy; + class GetMaxValue { + BasicValueFactory &BVF; + + public: + GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} + Optional operator()(QualType Ty) { + return BVF.getMaxValue(Ty).getLimitedValue(); + } + Optional operator()(Optional Ty) { + if (Ty) { + return operator()(*Ty); + } + return None; + } + } getMaxValue(BVF); + // These types are useful for writing specifications quickly, // New specifications should probably introduce more types. // Some types are hard to obtain from the AST, eg. "ssize_t". @@ -769,38 +879,30 @@ // 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 CharTy = ACtx.CharTy; + const QualType WCharTy = ACtx.WCharTy; 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 VoidPtrTy = getPointerTy(VoidTy); // void * + const QualType IntPtrTy = getPointerTy(IntTy); // int * const QualType UnsignedIntPtrTy = - ACtx.getPointerType(UnsignedIntTy); // unsigned int * - const QualType VoidPtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict - : VoidPtrTy; + 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 * - const QualType CharPtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict - : CharPtrTy; + getPointerTy(getConstTy(VoidTy)); // const void * + const QualType CharPtrTy = getPointerTy(CharTy); // char * + const QualType CharPtrRestrictTy = getRestrictTy(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 * + getPointerTy(getConstTy(CharTy)); // const char * + const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); + const QualType Wchar_tPtrTy = getPointerTy(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; + getPointerTy(getConstTy(WCharTy)); // const wchar_t * + const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); const RangeInt UnsignedIntMax = @@ -840,11 +942,16 @@ // 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. + // 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) - return; + return false; for (Decl *D : LookupRes) { if (auto *FD = dyn_cast(D)) { if (S.matchesAndSet(FD)) { @@ -856,10 +963,15 @@ FD->print(llvm::errs()); llvm::errs() << "\n"; } - return; + return true; } } } + return false; + } + // Add the summary with the Signature explicitly given. + bool operator()(StringRef Name, Signature Sign, Summary Sum) { + return operator()(Name, Sum.setSignature(Sign)); } // Add several summaries for the given name. void operator()(StringRef Name, const std::vector &Summaries) { @@ -868,32 +980,6 @@ } } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); - // We are finally ready to define specifications for all supported functions. - // - // The signature needs to have the correct number of arguments. - // However, we insert `Irrelevant' when the type is insignificant. - // - // Argument ranges should always cover all variants. If return value - // is completely unknown, omit it from the respective range set. - // - // All types in the spec need to be canonical. - // - // Every item in the list of range sets represents a particular - // execution path the analyzer would need to explore once - // the call is modeled - a new program state is constructed - // for every range set, and each range line in the range set - // corresponds to a specific constraint within this state. - // - // Upon comparing to another argument, the other argument is casted - // to the current argument's type. This avoids proper promotion but - // seems useful. For example, read() receives size_t argument, - // and its return value, which is of type ssize_t, cannot be greater - // than this argument. If we made a promotion, and the size argument - // is equal to, say, 10, then we'd impose a range of [0, 10] on the - // return value, however the correct range is [-1, 10]. - // - // Please update the list of functions in the header after editing! - // Below are helpers functions to create the summaries. auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges) { @@ -910,9 +996,16 @@ return std::make_shared(Ret, Op, OtherArgN); } } ReturnValueCondition; - auto Range = [](RangeInt b, RangeInt e) { - return IntRangeVector{std::pair{b, e}}; - }; + struct { + auto operator()(RangeInt b, RangeInt e) { + return IntRangeVector{std::pair{b, e}}; + } + auto operator()(RangeInt b, Optional e) { + if (e) + return IntRangeVector{std::pair{b, *e}}; + return IntRangeVector{}; + } + } Range; auto SingleValue = [](RangeInt v) { return IntRangeVector{std::pair{v, v}}; }; @@ -921,20 +1014,13 @@ return std::make_shared(ArgN); }; - Optional FileTy = lookupType("FILE", ACtx); - Optional FilePtrTy, FilePtrRestrictTy; - if (FileTy) { - // FILE * - FilePtrTy = ACtx.getPointerType(*FileTy); - // FILE *restrict - FilePtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy; - } + Optional FileTy = lookupTy("FILE"); + Optional FilePtrTy = getPointerTy(FileTy); + Optional FilePtrRestrictTy = getRestrictTy(FilePtrTy); - using RetType = QualType; // Templates for summaries that are reused by many functions. auto Getc = [&]() { - return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) + return Summary(ArgTypes{FilePtrTy}, RetType{IntTy}, NoEvalCall) .Case({ReturnValueCondition(WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})}); }; @@ -946,7 +1032,7 @@ }; auto Fread = [&]() { return Summary( - ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy}, + ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy}, RetType{SizeTy}, NoEvalCall) .Case({ ReturnValueCondition(LessThanOrEq, ArgNo(2)), @@ -955,7 +1041,7 @@ }; auto Fwrite = [&]() { return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy, - *FilePtrRestrictTy}, + FilePtrRestrictTy}, RetType{SizeTy}, NoEvalCall) .Case({ ReturnValueCondition(LessThanOrEq, ArgNo(2)), @@ -968,6 +1054,17 @@ .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})}); }; + // We are finally ready to define specifications for all supported functions. + // + // Argument ranges should always cover all variants. If return value + // is completely unknown, omit it from the respective range set. + // + // Every item in the list of range sets represents a particular + // execution path the analyzer would need to explore once + // the call is modeled - a new program state is constructed + // for every range set, and each range line in the range set + // corresponds to a specific constraint within this state. + // The isascii() family of functions. // The behavior is undefined if the value of the argument is not // representable as unsigned char or is not equal to EOF. See e.g. C99 @@ -1103,20 +1200,16 @@ ReturnValueCondition(WithinRange, SingleValue(0))})); // The getc() family of functions that returns either a char or an EOF. - if (FilePtrTy) { addToFunctionSummaryMap("getc", Getc()); addToFunctionSummaryMap("fgetc", Getc()); - } addToFunctionSummaryMap( "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall) .Case({ReturnValueCondition( WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})})); // read()-like functions that never return more than buffer size. - if (FilePtrRestrictTy) { addToFunctionSummaryMap("fread", Fread()); addToFunctionSummaryMap("fwrite", Fwrite()); - } // We are not sure how ssize_t is defined on every platform, so we // provide three variants that should cover common cases. @@ -1194,14 +1287,13 @@ .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional Off_tTy = lookupType("off_t", ACtx); + Optional Off_tTy = lookupTy("off_t"); - 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 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", @@ -1219,22 +1311,19 @@ .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)))); + // 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); + Optional Mode_tTy = lookupTy("mode_t"); - 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)))); + // 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( @@ -1243,16 +1332,13 @@ .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - Optional DirTy = lookupType("DIR", ACtx); - Optional DirPtrTy; - if (DirTy) - DirPtrTy = ACtx.getPointerType(*DirTy); + Optional DirTy = lookupTy("DIR"); + Optional DirPtrTy = getPointerTy(DirTy); - if (DirPtrTy) - // int dirfd(DIR *dirp); - addToFunctionSummaryMap( - "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int dirfd(DIR *dirp); + addToFunctionSummaryMap( + "dirfd", Summary(ArgTypes{DirPtrTy}, RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0)))); // unsigned int alarm(unsigned int seconds); addToFunctionSummaryMap( @@ -1261,11 +1347,10 @@ .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)))); + // 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}, @@ -1302,92 +1387,80 @@ .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 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)))); - } + // 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); + Optional Dev_tTy = lookupTy("dev_t"); - 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 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)))); - } + // 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 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 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)))); - } + // 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); + Optional Uid_tTy = lookupTy("uid_t"); + Optional Gid_tTy = lookupTy("gid_t"); - 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 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 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 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 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( @@ -1430,65 +1503,52 @@ .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; - } + Optional StructStatTy = lookupTy("stat"); + Optional StructStatPtrTy = getPointerTy(StructStatTy); + Optional StructStatPtrRestrictTy = getRestrictTy(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)))); + // 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 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)))); - // 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)))); + // DIR *opendir(const char *name); + addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy}, + RetType{DirPtrTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0)))); - // 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) + // DIR *fdopendir(int fd); + addToFunctionSummaryMap( + "fdopendir", Summary(ArgTypes{IntTy}, RetType{DirPtrTy}, 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)))); - } + Range(0, IntMax)))); // int isatty(int fildes); addToFunctionSummaryMap( @@ -1496,19 +1556,17 @@ .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)))); + // 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 pclose(FILE *stream); + addToFunctionSummaryMap( + "pclose", Summary(ArgTypes{FilePtrTy}, RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0)))); // int close(int fildes); addToFunctionSummaryMap( @@ -1528,26 +1586,22 @@ 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)))); + // 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)))); - // void seekdir(DIR *dirp, long loc); - addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy}, - RetType{VoidTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - } + // 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}, @@ -1570,100 +1624,86 @@ .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)))); + // int fileno(FILE *stream); + addToFunctionSummaryMap( + "fileno", Summary(ArgTypes{FilePtrTy}, 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)))); - } + // int fseeko(FILE *stream, off_t offset, int whence); + addToFunctionSummaryMap( + "fseeko", + Summary(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0)))); - if (Off_tTy) { - Optional Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue(); + // off_t ftello(FILE *stream); + addToFunctionSummaryMap( + "ftello", Summary(ArgTypes{FilePtrTy}, RetType{Off_tTy}, NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0)))); - // 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 Off_tMax = getMaxValue(Off_tTy); + // 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)))); - } + Optional Off64_tTy = lookupTy("off64_t"); + Optional Off64_tMax = getMaxValue(Off_tTy); + // 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)))); + // 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); + Optional Ssize_tTy = lookupTy("ssize_t"); - 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 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)))); - } + // 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); @@ -1680,7 +1720,7 @@ RetType{CharPtrTy}, NoEvalCall) .ArgConstraint(NotNull(ArgNo(0)))); - QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst()); + QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); // int execv(const char *path, char *const argv[]); addToFunctionSummaryMap("execv", @@ -1702,6 +1742,254 @@ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); + + Optional StructSockaddrTy = lookupTy("sockaddr"); + Optional StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); + Optional ConstStructSockaddrPtrTy = + getPointerTy(getConstTy(StructSockaddrTy)); + Optional StructSockaddrPtrRestrictTy = + getRestrictTy(StructSockaddrPtrTy); + Optional ConstStructSockaddrPtrRestrictTy = + getRestrictTy(ConstStructSockaddrPtrTy); + Optional Socklen_tTy = lookupTy("socklen_t"); + Optional Socklen_tPtrTy = getPointerTy(Socklen_tTy); + Optional Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy); + Optional Socklen_tMax = getMaxValue(Socklen_tTy); + + // 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. + auto Accept = + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))); + if (!addToFunctionSummaryMap( + "accept", + // int accept(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Accept)) + addToFunctionSummaryMap( + "accept", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Accept); + + // 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))))) + // Do not add constraints on sockaddr. + 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)))); + + auto Recvfrom = + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))); + if (!addToFunctionSummaryMap( + "recvfrom", + // ssize_t recvfrom(int socket, void *restrict buffer, + // size_t length, + // int flags, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, + StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{Ssize_tTy}), + Recvfrom)) + addToFunctionSummaryMap( + "recvfrom", + Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, + Irrelevant, Socklen_tPtrRestrictTy}, + RetType{Ssize_tTy}), + Recvfrom); + + auto Sendto = + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))); + if (!addToFunctionSummaryMap( + "sendto", + // ssize_t sendto(int socket, const void *message, size_t length, + // int flags, const struct sockaddr *dest_addr, + // socklen_t dest_len); + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, + ConstStructSockaddrPtrTy, Socklen_tTy}, + RetType{Ssize_tTy}), + Sendto)) + addToFunctionSummaryMap( + "sendto", + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant, + Socklen_tTy}, + RetType{Ssize_tTy}), + Sendto); + + // int listen(int sockfd, int backlog); + addToFunctionSummaryMap( + "listen", Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // 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 = lookupTy("msghdr"); + Optional StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + Optional ConstStructMsghdrPtrTy = + getPointerTy(getConstTy(StructMsghdrTy)); + + // 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)))); + + // 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)))); + + // 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)))); + + // 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)))); + + // 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)))); + + // 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-lookup.c b/clang/test/Analysis/std-c-library-functions-POSIX-lookup.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/std-c-library-functions-POSIX-lookup.c @@ -0,0 +1,22 @@ +// 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 --allow-empty + +// We test here the implementation of our summary API with Optional types. In +// this TU we do not provide declaration for any of the functions that have +// summaries. The implementation should be able to handle the nonexistent +// declarations in a way that the summary is not added to the map. We expect no +// crashes (i.e. no optionals should be 'dereferenced') and no output. + +// Must have at least one call expression to initialize the summary map. +int bar(void); +void foo() { + bar(); +} + +// CHECK-NOT: Loaded summary for: 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,46 @@ 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##__; +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() {