diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 12816eed2e8b5..b6f6dce29a87b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1060,6 +1060,9 @@ impact the linker behaviour like the other `-static-*` flags. Crash and bug fixes ^^^^^^^^^^^^^^^^^^^ +- Fixed a crash in ``UnixAPIMisuseChecker`` and ``MallocChecker`` when analyzing + code with non-standard ``getline`` or ``getdelim`` function signatures. + Improvements ^^^^^^^^^^^^ diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 22dd3f0374849..5c603dd7f1963 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -47,6 +47,7 @@ add_clang_library(clangStaticAnalyzerCheckers ExprInspectionChecker.cpp FixedAddressChecker.cpp FuchsiaHandleChecker.cpp + FunctionSignature.cpp GCDAntipatternChecker.cpp GenericTaintChecker.cpp GTestChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.cpp b/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.cpp new file mode 100644 index 0000000000000..64cfb9061617c --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.cpp @@ -0,0 +1,188 @@ +//=== FunctionSignature.cpp - Validation of functions signatures. --*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Provides utilities for validating function signatures. +// +//===----------------------------------------------------------------------===// + +#include "FunctionSignature.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +namespace clang { +namespace ento { + +Signature::Signature(ArgTypes ArgTys, RetType RetTy) { + for (const std::optional &Arg : ArgTys) { + if (!Arg) { + Invalid = true; + return; + } + assertArgTypeSuitableForSignature(*Arg); + this->ArgTys.push_back(*Arg); + } + if (!RetTy) { + Invalid = true; + return; + } + assertRetTypeSuitableForSignature(*RetTy); + this->RetTy = *RetTy; +} + +bool Signature::isInvalid() const { return Invalid; } + +bool Signature::matches(const FunctionDecl *FD) const { + assert(!isInvalid()); + // Check the number of arguments. + if (FD->param_size() != ArgTys.size()) + return false; + + // The "restrict" keyword is illegal in C++, however, many libc + // implementations use the "__restrict" compiler intrinsic in functions + // prototypes. The "__restrict" keyword qualifies a type as a restricted type + // even in C++. + // In case of any non-C99 languages, we don't want to match based on the + // restrict qualifier because we cannot know if the given libc implementation + // qualifies the paramater type or not. + const auto RemoveRestrict = [&FD](QualType T) { + if (!FD->getASTContext().getLangOpts().C99) + T.removeLocalRestrict(); + return T; + }; + + // Check the return type. + if (!isIrrelevant(RetTy)) { + const QualType FDRetTy = + RemoveRestrict(FD->getReturnType().getCanonicalType()); + if (RetTy != FDRetTy) + return false; + } + + // Check the argument types. + for (auto [Idx, ArgTy] : llvm::enumerate(ArgTys)) { + if (isIrrelevant(ArgTy)) + continue; + const QualType FDArgTy = + RemoveRestrict(FD->getParamDecl(Idx)->getType().getCanonicalType()); + if (ArgTy != FDArgTy) + return false; + } + + return true; +} + +bool Signature::isIrrelevant(QualType T) { return T.isNull(); } + +void Signature::assertArgTypeSuitableForSignature(QualType T) { + assert((T.isNull() || !T->isVoidType()) && + "We should have no void types in the spec"); + assert((T.isNull() || T.isCanonical()) && + "We should only have canonical types in the spec"); +} + +void Signature::assertRetTypeSuitableForSignature(QualType T) { + assert((T.isNull() || T.isCanonical()) && + "We should only have canonical types in the spec"); +} + +TypeFactory::TypeFactory(const ASTContext &ACtx) : ACtx(ACtx) {} + +QualType TypeFactory::getVoidTy() const { return ACtx.VoidTy; } +QualType TypeFactory::getCharTy() const { return ACtx.CharTy; } +QualType TypeFactory::getWCharTy() const { return ACtx.WCharTy; } +QualType TypeFactory::getIntTy() const { return ACtx.IntTy; } +QualType TypeFactory::getUnsignedIntTy() const { return ACtx.UnsignedIntTy; } +QualType TypeFactory::getLongTy() const { return ACtx.LongTy; } +QualType TypeFactory::getSizeTy() const { return ACtx.getSizeType(); } + +QualType TypeFactory::getPointerTy(QualType Ty) const { + return ACtx.getPointerType(Ty); +} + +std::optional +TypeFactory::getPointerTy(std::optional Ty) const { + return Ty ? std::optional(getPointerTy(*Ty)) : std::nullopt; +} + +QualType TypeFactory::getConstTy(QualType Ty) const { return Ty.withConst(); } + +std::optional +TypeFactory::getConstTy(std::optional Ty) const { + return Ty ? std::optional(getConstTy(*Ty)) : std::nullopt; +} + +QualType TypeFactory::getRestrictTy(QualType Ty) const { + return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; +} + +std::optional +TypeFactory::getRestrictTy(std::optional Ty) const { + return Ty ? std::optional(getRestrictTy(*Ty)) : std::nullopt; +} + +std::optional TypeFactory::lookupTy(StringRef Name) const { + IdentifierInfo &II = ACtx.Idents.get(Name); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + if (LookupRes.empty()) + return std::nullopt; + + // Prioritize 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 std::nullopt; +} + +QualType TypeFactory::getVoidPtrTy() const { return getPointerTy(getVoidTy()); } + +QualType TypeFactory::getCharPtrTy() const { return getPointerTy(getCharTy()); } + +QualType TypeFactory::getConstCharPtrTy() const { + return getPointerTy(getConstTy(getCharTy())); +} + +QualType TypeFactory::getConstVoidPtrTy() const { + return getPointerTy(getConstTy(getVoidTy())); +} + +void SignatureMatcher::addSignature(StringRef Name, const Signature &Sign) { + Signatures.try_emplace(Name, Sign); +} + +bool SignatureMatcher::matches(const FunctionDecl *FD, + StringRef ExpectedName) const { + if (FD->getName() != ExpectedName) + return false; + + const auto It = Signatures.find(ExpectedName); + if (It == Signatures.end()) + return false; + + const Signature &Sign = It->second; + if (Sign.isInvalid()) + return false; + + return Sign.matches(FD); +} + +} // namespace ento +} // namespace clang \ No newline at end of file diff --git a/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.h b/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.h new file mode 100644 index 0000000000000..5c38ddd3596d4 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/FunctionSignature.h @@ -0,0 +1,117 @@ +//=== FunctionSignature.h - Validation of functions signatures. ----*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Provides utilities for validating function signatures. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_FUNCTIONSIGNATURE_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_FUNCTIONSIGNATURE_H + +#include + +#include "clang/AST/Type.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +class FunctionDecl; +class ASTContext; +class IdentifierInfo; + +namespace ento { + +// The signature of a function we want to check. 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. +// All types in the spec need to be canonical. +class Signature { + using ArgQualTypes = SmallVector; + ArgQualTypes ArgTys; + QualType RetTy; + // True if any component type is not found by lookup. + bool Invalid = false; + +public: + using ArgTypes = ArrayRef>; + using RetType = std::optional; + + // 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); + Signature(const Signature &) = default; + Signature(Signature &&) = default; + Signature &operator=(const Signature &) = default; + Signature &operator=(Signature &&) = default; + + bool isInvalid() const; + bool matches(const FunctionDecl *FD) const; + +private: + static bool isIrrelevant(QualType T); + static void assertArgTypeSuitableForSignature(QualType T); + static void assertRetTypeSuitableForSignature(QualType T); +}; + +/// Provides type creation utilities for Signature class. +/// It encapsulates the logic for creating pointer, const, restrict types +/// and looking up types by name from the AST. +class TypeFactory { + const ASTContext &ACtx; + +public: + explicit TypeFactory(const ASTContext &ACtx); + + // Basic types from AST + QualType getVoidTy() const; + QualType getCharTy() const; + QualType getWCharTy() const; + QualType getIntTy() const; + QualType getUnsignedIntTy() const; + QualType getLongTy() const; + QualType getSizeTy() const; + + // Common type mutations + QualType getPointerTy(QualType Ty) const; + std::optional getPointerTy(std::optional Ty) const; + QualType getConstTy(QualType Ty) const; + std::optional getConstTy(std::optional Ty) const; + QualType getRestrictTy(QualType Ty) const; + std::optional getRestrictTy(std::optional Ty) const; + + // Type lookup by name in AST + std::optional lookupTy(StringRef Name) const; + + // Common composite types + QualType getVoidPtrTy() const; + QualType getCharPtrTy() const; + QualType getConstCharPtrTy() const; + QualType getConstVoidPtrTy() const; +}; + +/// Helper class for matching function signatures. +/// This class stores function signatures and provides an API to check +/// if a given FunctionDecl matches the expected signature. +class SignatureMatcher { + using SignatureMap = llvm::StringMap; + SignatureMap Signatures; + +public: + // Add a function signature to the matcher. + void addSignature(StringRef Name, const Signature &Sign); + + // Check if a function declaration matches the expected signature. + bool matches(const FunctionDecl *FD, StringRef ExpectedName) const; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_FUNCTIONSIGNATURE_H \ No newline at end of file diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 35e98a5e2719a..e7ea73733525e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -45,6 +45,7 @@ //===----------------------------------------------------------------------===// #include "AllocationState.h" +#include "FunctionSignature.h" #include "InterCheckerAPI.h" #include "NoOwnershipChangeVisitor.h" #include "clang/AST/Attr.h" @@ -412,6 +413,9 @@ class MallocChecker mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; mutable std::unique_ptr BT_TaintedAlloc; + + mutable SignatureMatcher FunctionSignatureMatcher; + mutable bool SignaturesInitialized = false; #define CHECK_FN(NAME) \ void NAME(ProgramStateRef State, const CallEvent &Call, CheckerContext &C) \ @@ -455,6 +459,9 @@ class MallocChecker {{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::checkGetdelim}, {{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::checkGetdelim}, }; + + void initSignatures(CheckerContext &C) const; + bool IsGetDelim(const CallEvent &Call, CheckerContext &C) const; const CallDescriptionMap FreeingMemFnMap{ {{CDM::CLibrary, {"free"}, 1}, &MallocChecker::checkFree}, @@ -1482,11 +1489,51 @@ static bool isFromStdNamespace(const CallEvent &Call) { return FD->isInStdNamespace(); } + +void MallocChecker::initSignatures(CheckerContext &C) const { + if (SignaturesInitialized) + return; + SignaturesInitialized = true; + + const ASTContext &ACtx = C.getASTContext(); + TypeFactory TF(ACtx); + + // Create signatures for getline and getdelim + // ssize_t getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream); + // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, int delimiter, FILE *restrict stream); + + std::optional Ssize_tTy = TF.lookupTy("ssize_t"); + std::optional FileTy = TF.lookupTy("FILE"); + std::optional FilePtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(FileTy)); + QualType CharPtrPtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(TF.getCharPtrTy())); + QualType SizePtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(TF.getSizeTy())); + + Signature GetlineSign( + Signature::ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, + Ssize_tTy); + FunctionSignatureMatcher.addSignature("getline", GetlineSign); + + Signature GetdelimSign( + Signature::ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, TF.getIntTy(), FilePtrRestrictTy}, + Ssize_tTy); + FunctionSignatureMatcher.addSignature("getdelim", GetdelimSign); +} + +bool MallocChecker::IsGetDelim(const CallEvent &Call, CheckerContext &C) const { + const FunctionDecl *FD = dyn_cast_if_present(Call.getDecl()); + if (!FD || FD->getKind() != Decl::Function) + return false; + + initSignatures(C); + return FunctionSignatureMatcher.matches(FD, "getdelim") || + FunctionSignatureMatcher.matches(FD, "getline"); +} + void MallocChecker::preGetdelim(ProgramStateRef State, const CallEvent &Call, CheckerContext &C) const { // Discard calls to the C++ standard library function std::getline(), which // is completely unrelated to the POSIX getline() that we're checking. - if (isFromStdNamespace(Call)) + if (isFromStdNamespace(Call) || !IsGetDelim(Call, C)) return; const auto LinePtr = getPointeeVal(Call.getArgSVal(0), State); @@ -1509,7 +1556,7 @@ void MallocChecker::checkGetdelim(ProgramStateRef State, const CallEvent &Call, CheckerContext &C) const { // Discard calls to the C++ standard library function std::getline(), which // is completely unrelated to the POSIX getline() that we're checking. - if (isFromStdNamespace(Call)) + if (isFromStdNamespace(Call) || !IsGetDelim(Call, C)) return; // Handle the post-conditions of getline and getdelim: diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 1c748f9bc1828..e406f88acc421 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -41,6 +41,7 @@ //===----------------------------------------------------------------------===// #include "ErrnoModeling.h" +#include "FunctionSignature.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -49,7 +50,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FormatVariadic.h" @@ -670,62 +670,9 @@ class StdLibraryFunctionsChecker StringRef getNote() const { return Note; } }; - using ArgTypes = ArrayRef>; - using RetType = std::optional; - // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. const QualType Irrelevant{}; - bool static isIrrelevant(QualType T) { return T.isNull(); } - - // 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. - // 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 (std::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: - static void assertArgTypeSuitableForSignature(QualType T) { - assert((T.isNull() || !T->isVoidType()) && - "We should have no void types in the spec"); - assert((T.isNull() || T.isCanonical()) && - "We should only have canonical types in the spec"); - } - static void assertRetTypeSuitableForSignature(QualType T) { - assert((T.isNull() || T.isCanonical()) && - "We should only have canonical types in the spec"); - } - }; static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { assert(FD && "Function must be set"); @@ -1494,46 +1441,6 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, llvm_unreachable("Unknown invalidation kind!"); } -bool StdLibraryFunctionsChecker::Signature::matches( - const FunctionDecl *FD) const { - assert(!isInvalid()); - // Check the number of arguments. - if (FD->param_size() != ArgTys.size()) - return false; - - // The "restrict" keyword is illegal in C++, however, many libc - // implementations use the "__restrict" compiler intrinsic in functions - // prototypes. The "__restrict" keyword qualifies a type as a restricted type - // even in C++. - // In case of any non-C99 languages, we don't want to match based on the - // restrict qualifier because we cannot know if the given libc implementation - // qualifies the paramter type or not. - auto RemoveRestrict = [&FD](QualType T) { - if (!FD->getASTContext().getLangOpts().C99) - T.removeLocalRestrict(); - return T; - }; - - // Check the return type. - if (!isIrrelevant(RetTy)) { - QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType()); - if (RetTy != FDRetTy) - return false; - } - - // Check the argument types. - for (auto [Idx, ArgTy] : llvm::enumerate(ArgTys)) { - if (isIrrelevant(ArgTy)) - continue; - QualType FDArgTy = - RemoveRestrict(FD->getParamDecl(Idx)->getType().getCanonicalType()); - if (ArgTy != FDArgTy) - return false; - } - - return true; -} - std::optional StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, CheckerContext &C) const { @@ -1568,75 +1475,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( const ASTContext &ACtx = BVF.getContext(); Preprocessor &PP = C.getPreprocessor(); - // 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. - std::optional operator()(StringRef Name) { - IdentifierInfo &II = ACtx.Idents.get(Name); - auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); - if (LookupRes.empty()) - return std::nullopt; - - // Prioritize 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 std::nullopt; - } - } lookupTy(ACtx); - - // Below are auxiliary classes to handle optional types that we get as a - // result of the lookup. - class GetRestrictTy { - const ASTContext &ACtx; + TypeFactory TF(ACtx); - public: - GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} - QualType operator()(QualType Ty) { - return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; - } - std::optional operator()(std::optional Ty) { - if (Ty) - return operator()(*Ty); - return std::nullopt; - } - } getRestrictTy(ACtx); - class GetPointerTy { - const ASTContext &ACtx; - - public: - GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} - QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } - std::optional operator()(std::optional Ty) { - if (Ty) - return operator()(*Ty); - return std::nullopt; - } - } getPointerTy(ACtx); - class { - public: - std::optional operator()(std::optional Ty) { - return Ty ? std::optional(Ty->withConst()) : std::nullopt; - } - QualType operator()(QualType Ty) { return Ty.withConst(); } - } getConstTy; class GetMaxValue { BasicValueFactory &BVF; @@ -1660,32 +1500,29 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // of function summary for common cases (eg. ssize_t could be int or long // or long long, so three summary variants would be enough). // Of course, function variants are also useful for C++ overloads. - const QualType VoidTy = ACtx.VoidTy; - const QualType 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 SizeTy = ACtx.getSizeType(); - - const QualType VoidPtrTy = getPointerTy(VoidTy); // void * - const QualType IntPtrTy = getPointerTy(IntTy); // int * + const QualType VoidTy = TF.getVoidTy(); + const QualType WCharTy = TF.getWCharTy(); + const QualType IntTy = TF.getIntTy(); + const QualType UnsignedIntTy = TF.getUnsignedIntTy(); + const QualType LongTy = TF.getLongTy(); + const QualType SizeTy = TF.getSizeTy(); + + const QualType VoidPtrTy = TF.getVoidPtrTy(); // void * + const QualType IntPtrTy = TF.getPointerTy(IntTy); // int * const QualType UnsignedIntPtrTy = - getPointerTy(UnsignedIntTy); // unsigned int * - const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); - const QualType ConstVoidPtrTy = - getPointerTy(getConstTy(VoidTy)); // const void * - const QualType CharPtrTy = getPointerTy(CharTy); // char * - const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); - const QualType ConstCharPtrTy = - getPointerTy(getConstTy(CharTy)); // const char * - const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); - const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t * + TF.getPointerTy(UnsignedIntTy); // unsigned int * + const QualType VoidPtrRestrictTy = TF.getRestrictTy(TF.getVoidPtrTy()); + const QualType ConstVoidPtrTy = TF.getConstVoidPtrTy(); // const void * + const QualType CharPtrTy = TF.getCharPtrTy(); // char * + const QualType CharPtrRestrictTy = TF.getRestrictTy(TF.getCharPtrTy()); + const QualType ConstCharPtrTy = TF.getConstCharPtrTy(); // const char * + const QualType ConstCharPtrRestrictTy = TF.getRestrictTy(TF.getConstCharPtrTy()); + const QualType Wchar_tPtrTy = TF.getPointerTy(WCharTy); // wchar_t * const QualType ConstWchar_tPtrTy = - getPointerTy(getConstTy(WCharTy)); // const wchar_t * - const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); - const QualType SizePtrTy = getPointerTy(SizeTy); - const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); + TF.getPointerTy(TF.getConstTy(WCharTy)); // const wchar_t * + const QualType ConstVoidPtrRestrictTy = TF.getRestrictTy(TF.getConstVoidPtrTy()); + const QualType SizePtrTy = TF.getPointerTy(SizeTy); + const QualType SizePtrRestrictTy = TF.getRestrictTy(SizePtrTy); const RangeInt IntMax = BVF.getMaxValue(IntTy)->getLimitedValue(); const RangeInt UnsignedIntMax = @@ -1803,19 +1640,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( SizeArg2N); }; - std::optional FileTy = lookupTy("FILE"); - std::optional FilePtrTy = getPointerTy(FileTy); - std::optional FilePtrRestrictTy = getRestrictTy(FilePtrTy); + std::optional FileTy = TF.lookupTy("FILE"); + std::optional FilePtrTy = TF.getPointerTy(FileTy); + std::optional FilePtrRestrictTy = TF.getRestrictTy(FilePtrTy); - std::optional FPosTTy = lookupTy("fpos_t"); - std::optional FPosTPtrTy = getPointerTy(FPosTTy); - std::optional ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); - std::optional FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); + std::optional FPosTTy = TF.lookupTy("fpos_t"); + std::optional FPosTPtrTy = TF.getPointerTy(FPosTTy); + std::optional ConstFPosTPtrTy = + TF.getPointerTy(TF.getConstTy(FPosTTy)); + std::optional FPosTPtrRestrictTy = TF.getRestrictTy(FPosTPtrTy); constexpr llvm::StringLiteral GenericSuccessMsg( "Assuming that '{0}' is successful"); constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails"); + using ArgTypes = Signature::ArgTypes; + using RetType = Signature::RetType; + // We are finally ready to define specifications for all supported functions. // // Argument ranges should always cover all variants. If return value @@ -2070,7 +1911,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{SizeTy}), FreadSummary); - std::optional Ssize_tTy = lookupTy("ssize_t"); + std::optional Ssize_tTy = TF.lookupTy("ssize_t"); std::optional Ssize_tMax = getMaxValue(Ssize_tTy); auto ReadSummary = @@ -2097,7 +1938,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Range({-1, -1}, {1, Ssize_tMax}))}, ErrnoIrrelevant); - QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); + QualType CharPtrPtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(TF.getCharPtrTy())); // getline()-like functions either fail or read at least the delimiter. // FIXME these are actually defined by POSIX and not by the C standard, we @@ -2238,7 +2079,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - std::optional Off_tTy = lookupTy("off_t"); + std::optional Off_tTy = TF.lookupTy("off_t"); std::optional Off_tMax = getMaxValue(Off_tTy); // int fgetc(FILE *stream); @@ -2565,7 +2406,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - std::optional Mode_tTy = lookupTy("mode_t"); + std::optional Mode_tTy = TF.lookupTy("mode_t"); // int creat(const char *pathname, mode_t mode); addToFunctionSummaryMap( @@ -2583,8 +2424,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - std::optional DirTy = lookupTy("DIR"); - std::optional DirPtrTy = getPointerTy(DirTy); + std::optional DirTy = TF.lookupTy("DIR"); + std::optional DirPtrTy = TF.getPointerTy(DirTy); // int dirfd(DIR *dirp); addToFunctionSummaryMap( @@ -2690,7 +2531,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); - std::optional Dev_tTy = lookupTy("dev_t"); + std::optional Dev_tTy = TF.lookupTy("dev_t"); // int mknod(const char *pathname, mode_t mode, dev_t dev); addToFunctionSummaryMap( @@ -2740,8 +2581,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - std::optional Uid_tTy = lookupTy("uid_t"); - std::optional Gid_tTy = lookupTy("gid_t"); + std::optional Uid_tTy = TF.lookupTy("uid_t"); + std::optional Gid_tTy = TF.lookupTy("gid_t"); // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, // int flags); @@ -2840,10 +2681,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); - std::optional StructStatTy = lookupTy("stat"); - std::optional StructStatPtrTy = getPointerTy(StructStatTy); + std::optional StructStatTy = TF.lookupTy("stat"); + std::optional StructStatPtrTy = TF.getPointerTy(StructStatTy); std::optional StructStatPtrRestrictTy = - getRestrictTy(StructStatPtrTy); + TF.getRestrictTy(StructStatPtrTy); // int fstat(int fd, struct stat *statbuf); addToFunctionSummaryMap( @@ -2964,7 +2805,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - std::optional Off64_tTy = lookupTy("off64_t"); + std::optional Off64_tTy = TF.lookupTy("off64_t"); // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, // off64_t offset); // FIXME: Improve for errno modeling. @@ -3073,7 +2914,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); + QualType CharPtrConstPtr = TF.getPointerTy(TF.getConstTy(CharPtrTy)); // int execv(const char *path, char *const argv[]); addToFunctionSummaryMap( @@ -3103,19 +2944,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); - std::optional StructSockaddrTy = lookupTy("sockaddr"); + std::optional StructSockaddrTy = TF.lookupTy("sockaddr"); std::optional StructSockaddrPtrTy = - getPointerTy(StructSockaddrTy); + TF.getPointerTy(StructSockaddrTy); std::optional ConstStructSockaddrPtrTy = - getPointerTy(getConstTy(StructSockaddrTy)); + TF.getPointerTy(TF.getConstTy(StructSockaddrTy)); std::optional StructSockaddrPtrRestrictTy = - getRestrictTy(StructSockaddrPtrTy); + TF.getRestrictTy(StructSockaddrPtrTy); std::optional ConstStructSockaddrPtrRestrictTy = - getRestrictTy(ConstStructSockaddrPtrTy); - std::optional Socklen_tTy = lookupTy("socklen_t"); - std::optional Socklen_tPtrTy = getPointerTy(Socklen_tTy); + TF.getRestrictTy(ConstStructSockaddrPtrTy); + std::optional Socklen_tTy = TF.lookupTy("socklen_t"); + std::optional Socklen_tPtrTy = TF.getPointerTy(Socklen_tTy); std::optional Socklen_tPtrRestrictTy = - getRestrictTy(Socklen_tPtrTy); + TF.getRestrictTy(Socklen_tPtrTy); std::optional Socklen_tMax = getMaxValue(Socklen_tTy); // In 'socket.h' of some libc implementations with C99, sockaddr parameter @@ -3334,10 +3175,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))); - std::optional StructMsghdrTy = lookupTy("msghdr"); - std::optional StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + std::optional StructMsghdrTy = TF.lookupTy("msghdr"); + std::optional StructMsghdrPtrTy = TF.getPointerTy(StructMsghdrTy); std::optional ConstStructMsghdrPtrTy = - getPointerTy(getConstTy(StructMsghdrTy)); + TF.getPointerTy(TF.getConstTy(StructMsghdrTy)); // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); addToFunctionSummaryMap( @@ -3454,8 +3295,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); - std::optional StructUtimbufTy = lookupTy("utimbuf"); - std::optional StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); + std::optional StructUtimbufTy = TF.lookupTy("utimbuf"); + std::optional StructUtimbufPtrTy = + TF.getPointerTy(StructUtimbufTy); // int utime(const char *filename, struct utimbuf *buf); addToFunctionSummaryMap( @@ -3466,11 +3308,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - std::optional StructTimespecTy = lookupTy("timespec"); + std::optional StructTimespecTy = TF.lookupTy("timespec"); std::optional StructTimespecPtrTy = - getPointerTy(StructTimespecTy); + TF.getPointerTy(StructTimespecTy); std::optional ConstStructTimespecPtrTy = - getPointerTy(getConstTy(StructTimespecTy)); + TF.getPointerTy(TF.getConstTy(StructTimespecTy)); // int futimens(int fd, const struct timespec times[2]); addToFunctionSummaryMap( @@ -3494,9 +3336,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(1)))); - std::optional StructTimevalTy = lookupTy("timeval"); + std::optional StructTimevalTy = TF.lookupTy("timeval"); std::optional ConstStructTimevalPtrTy = - getPointerTy(getConstTy(StructTimevalTy)); + TF.getPointerTy(TF.getConstTy(StructTimevalTy)); // int utimes(const char *filename, const struct timeval times[2]); addToFunctionSummaryMap( @@ -3518,20 +3360,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - std::optional Time_tTy = lookupTy("time_t"); + std::optional Time_tTy = TF.lookupTy("time_t"); std::optional ConstTime_tPtrTy = - getPointerTy(getConstTy(Time_tTy)); + TF.getPointerTy(TF.getConstTy(Time_tTy)); std::optional ConstTime_tPtrRestrictTy = - getRestrictTy(ConstTime_tPtrTy); + TF.getRestrictTy(ConstTime_tPtrTy); - std::optional StructTmTy = lookupTy("tm"); - std::optional StructTmPtrTy = getPointerTy(StructTmTy); + std::optional StructTmTy = TF.lookupTy("tm"); + std::optional StructTmPtrTy = TF.getPointerTy(StructTmTy); std::optional StructTmPtrRestrictTy = - getRestrictTy(StructTmPtrTy); + TF.getRestrictTy(StructTmPtrTy); std::optional ConstStructTmPtrTy = - getPointerTy(getConstTy(StructTmTy)); + TF.getPointerTy(TF.getConstTy(StructTmTy)); std::optional ConstStructTmPtrRestrictTy = - getRestrictTy(ConstStructTmPtrTy); + TF.getRestrictTy(ConstStructTmPtrTy); // struct tm * localtime(const time_t *tp); addToFunctionSummaryMap( @@ -3586,7 +3428,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - std::optional Clockid_tTy = lookupTy("clockid_t"); + std::optional Clockid_tTy = TF.lookupTy("clockid_t"); // int clock_gettime(clockid_t clock_id, struct timespec *tp); addToFunctionSummaryMap( @@ -3597,9 +3439,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(1)))); - std::optional StructItimervalTy = lookupTy("itimerval"); + std::optional StructItimervalTy = TF.lookupTy("itimerval"); std::optional StructItimervalPtrTy = - getPointerTy(StructItimervalTy); + TF.getPointerTy(StructItimervalTy); // int getitimer(int which, struct itimerval *curr_value); addToFunctionSummaryMap( @@ -3610,33 +3452,33 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(1)))); - std::optional Pthread_cond_tTy = lookupTy("pthread_cond_t"); + std::optional Pthread_cond_tTy = TF.lookupTy("pthread_cond_t"); std::optional Pthread_cond_tPtrTy = - getPointerTy(Pthread_cond_tTy); - std::optional Pthread_tTy = lookupTy("pthread_t"); - std::optional Pthread_tPtrTy = getPointerTy(Pthread_tTy); + TF.getPointerTy(Pthread_cond_tTy); + std::optional Pthread_tTy = TF.lookupTy("pthread_t"); + std::optional Pthread_tPtrTy = TF.getPointerTy(Pthread_tTy); std::optional Pthread_tPtrRestrictTy = - getRestrictTy(Pthread_tPtrTy); - std::optional Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); + TF.getRestrictTy(Pthread_tPtrTy); + std::optional Pthread_mutex_tTy = TF.lookupTy("pthread_mutex_t"); std::optional Pthread_mutex_tPtrTy = - getPointerTy(Pthread_mutex_tTy); + TF.getPointerTy(Pthread_mutex_tTy); std::optional Pthread_mutex_tPtrRestrictTy = - getRestrictTy(Pthread_mutex_tPtrTy); - std::optional Pthread_attr_tTy = lookupTy("pthread_attr_t"); + TF.getRestrictTy(Pthread_mutex_tPtrTy); + std::optional Pthread_attr_tTy = TF.lookupTy("pthread_attr_t"); std::optional Pthread_attr_tPtrTy = - getPointerTy(Pthread_attr_tTy); + TF.getPointerTy(Pthread_attr_tTy); std::optional ConstPthread_attr_tPtrTy = - getPointerTy(getConstTy(Pthread_attr_tTy)); + TF.getPointerTy(TF.getConstTy(Pthread_attr_tTy)); std::optional ConstPthread_attr_tPtrRestrictTy = - getRestrictTy(ConstPthread_attr_tPtrTy); + TF.getRestrictTy(ConstPthread_attr_tPtrTy); std::optional Pthread_mutexattr_tTy = - lookupTy("pthread_mutexattr_t"); + TF.lookupTy("pthread_mutexattr_t"); std::optional ConstPthread_mutexattr_tPtrTy = - getPointerTy(getConstTy(Pthread_mutexattr_tTy)); + TF.getPointerTy(TF.getConstTy(Pthread_mutexattr_tTy)); std::optional ConstPthread_mutexattr_tPtrRestrictTy = - getRestrictTy(ConstPthread_mutexattr_tPtrTy); + TF.getRestrictTy(ConstPthread_mutexattr_tPtrTy); - QualType PthreadStartRoutineTy = getPointerTy( + QualType PthreadStartRoutineTy = TF.getPointerTy( ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, FunctionProtoType::ExtProtoInfo())); diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 79d10d99e11d0..b9dae24f6a917 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "FunctionSignature.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -61,6 +62,10 @@ class UnixAPIMisuseChecker : public Checker { categories::UnixAPI}; const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI}; const std::optional Val_O_CREAT; + + // Signature matcher for getline/getdelim functions + mutable SignatureMatcher FunctionSignatureMatcher; + mutable bool SignaturesInitialized = false; ProgramStateRef EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, @@ -75,6 +80,8 @@ class UnixAPIMisuseChecker : public Checker { public: UnixAPIMisuseChecker(const ASTContext &Ctx, const Preprocessor &PP) : Val_O_CREAT(getCreateFlagValue(Ctx, PP)) {} + + void initSignatures(const ASTContext &ACtx) const; void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr, BugReporter &BR) const; @@ -91,6 +98,10 @@ class UnixAPIMisuseChecker : public Checker { void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg, SourceRange SR) const; + +private: + bool IsGetDelim(const CallEvent &Call, const FunctionDecl *FD, + CheckerContext &C) const; }; class UnixAPIPortabilityChecker : public Checker< check::PreStmt > { @@ -147,6 +158,42 @@ ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull( return PtrNotNull; } + +void UnixAPIMisuseChecker::initSignatures(const ASTContext &ACtx) const { + if (SignaturesInitialized) + return; + SignaturesInitialized = true; + + TypeFactory TF(ACtx); + + // ssize_t getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream); + // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, int delimiter, FILE *restrict stream); + + std::optional Ssize_tTy = TF.lookupTy("ssize_t"); + std::optional FileTy = TF.lookupTy("FILE"); + std::optional FilePtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(FileTy)); + QualType CharPtrPtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(TF.getCharPtrTy())); + QualType SizePtrRestrictTy = TF.getRestrictTy(TF.getPointerTy(TF.getSizeTy())); + + Signature GetlineSign( + Signature::ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, + Ssize_tTy); + FunctionSignatureMatcher.addSignature("getline", GetlineSign); + + Signature GetdelimSign( + Signature::ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, TF.getIntTy(), FilePtrRestrictTy}, + Ssize_tTy); + FunctionSignatureMatcher.addSignature("getdelim", GetdelimSign); +} + +bool UnixAPIMisuseChecker::IsGetDelim(const CallEvent &Call, const FunctionDecl *FD, + CheckerContext &C) const { + initSignatures(C.getASTContext()); + + return FunctionSignatureMatcher.matches(FD, "getdelim") || + FunctionSignatureMatcher.matches(FD, "getline"); +} + //===----------------------------------------------------------------------===// // "open" (man 2 open) //===----------------------------------------------------------------------===/ @@ -176,7 +223,7 @@ void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call, else if (FName == "pthread_once") CheckPthreadOnce(C, Call); - else if (is_contained({"getdelim", "getline"}, FName)) + else if (IsGetDelim(Call, FD, C)) CheckGetDelim(C, Call); } void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, diff --git a/clang/test/Analysis/getline-unixapi-invalid-signatures.c b/clang/test/Analysis/getline-unixapi-invalid-signatures.c new file mode 100644 index 0000000000000..9f8220f75c067 --- /dev/null +++ b/clang/test/Analysis/getline-unixapi-invalid-signatures.c @@ -0,0 +1,130 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_CORRECT +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_1 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_2 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_3 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_4 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_5 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_GH144884 + +// emulator of "system-header-simulator.h" because of redefinition of 'getline' function +typedef struct _FILE FILE; +typedef __typeof(sizeof(int)) size_t; +typedef long ssize_t; +#define NULL 0 + +int fclose(FILE *fp); +FILE *tmpfile(void); + +#ifdef TEST_CORRECT +ssize_t getline(char ** restrict lineptr, size_t * restrict n, FILE * restrict stream); +ssize_t getdelim(char ** restrict lineptr, size_t * restrict n, int delimiter, FILE * restrict stream); + +void test_correct() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getline(&buffer, NULL, F1); // expected-warning {{Size pointer might be NULL}} + fclose(F1); +} + +void test_delim_correct() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getdelim(&buffer, NULL, ',', F1); // expected-warning {{Size pointer might be NULL}} + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_1 +// expected-no-diagnostics +ssize_t getline(int lineptr); + +void test() { + FILE *F1 = tmpfile(); + if (!F1) + return; + int buffer = 0; + getline(buffer); + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_2 +// expected-no-diagnostics +ssize_t getline(char ** restrict lineptr, size_t * restrict n); + +void test() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getline(&buffer, NULL); + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_3 +// expected-no-diagnostics +ssize_t getline(char ** restrict lineptr, size_t n, FILE * restrict stream); + +void test() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getline(&buffer, 0, F1); + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_4 +// expected-no-diagnostics +ssize_t getline(char ** restrict lineptr, size_t * restrict n, int stream); +ssize_t getdelim(char ** restrict lineptr, size_t * restrict n, int delimiter, int stream); + +void test() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getline(&buffer, NULL, 1); + fclose(F1); +} + +void test_delim() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getdelim(&buffer, NULL, ',', 1); + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_5 +// expected-no-diagnostics +ssize_t getdelim(char ** restrict lineptr, size_t * restrict n, const char* delimiter, FILE * restrict stream); + +void test_delim() { + FILE *F1 = tmpfile(); + if (!F1) + return; + char *buffer = NULL; + getdelim(&buffer, NULL, ",", F1); + fclose(F1); +} +#endif + +#ifdef TEST_GETLINE_GH144884 +// expected-no-diagnostics +struct AW_string {}; +void getline(int *, struct AW_string); +void top() { + struct AW_string line; + int getline_file_info; + getline(&getline_file_info, line); +} +#endif