diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
| -rw-r--r-- | contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 237 | 
1 files changed, 80 insertions, 157 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 8bffada69b9b..44f4530781a8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -1,9 +1,8 @@  //= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//  // -//                     The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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  //  //===----------------------------------------------------------------------===//  // @@ -18,6 +17,7 @@  #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"  #include "clang/StaticAnalyzer/Core/Checker.h"  #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"  #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"  #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"  #include "llvm/ADT/STLExtras.h" @@ -58,7 +58,7 @@ public:    static void *getTag() { static int tag; return &tag; } -  bool evalCall(const CallExpr *CE, CheckerContext &C) const; +  bool evalCall(const CallEvent &Call, CheckerContext &C) const;    void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;    void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;    void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -73,7 +73,38 @@ public:    typedef void (CStringChecker::*FnCheck)(CheckerContext &,                                            const CallExpr *) const; +  CallDescriptionMap<FnCheck> Callbacks = { +      {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy}, +      {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy}, +      {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp}, +      {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove}, +      {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset}, +      {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset}, +      {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy}, +      {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy}, +      {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy}, +      {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy}, +      {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat}, +      {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat}, +      {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat}, +      {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength}, +      {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength}, +      {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp}, +      {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp}, +      {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp}, +      {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp}, +      {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep}, +      {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy}, +      {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp}, +      {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero}, +      {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero}, +  }; + +  // These require a bit of special handling. +  CallDescription StdCopy{{"std", "copy"}, 3}, +      StdCopyBackward{{"std", "copy_backward"}, 3}; +  FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;    void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;    void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;    void evalMemmove(CheckerContext &C, const CallExpr *CE) const; @@ -1201,9 +1232,6 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,  void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // void *memcpy(void *restrict dst, const void *restrict src, size_t n);    // The return value is the address of the destination buffer.    const Expr *Dest = CE->getArg(0); @@ -1213,9 +1241,6 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // void *mempcpy(void *restrict dst, const void *restrict src, size_t n);    // The return value is a pointer to the byte following the last written byte.    const Expr *Dest = CE->getArg(0); @@ -1225,9 +1250,6 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // void *memmove(void *dst, const void *src, size_t n);    // The return value is the address of the destination buffer.    const Expr *Dest = CE->getArg(0); @@ -1237,18 +1259,12 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // void bcopy(const void *src, void *dst, size_t n);    evalCopyCommon(C, CE, C.getState(),                   CE->getArg(2), CE->getArg(1), CE->getArg(0));  }  void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // int memcmp(const void *s1, const void *s2, size_t n);    CurrentFunctionDescription = "memory comparison function"; @@ -1323,18 +1339,12 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {  void CStringChecker::evalstrLength(CheckerContext &C,                                     const CallExpr *CE) const { -  if (CE->getNumArgs() < 1) -    return; -    // size_t strlen(const char *s);    evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);  }  void CStringChecker::evalstrnLength(CheckerContext &C,                                      const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    // size_t strnlen(const char *s, size_t maxlen);    evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);  } @@ -1459,9 +1469,6 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,  }  void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    // char *strcpy(char *restrict dst, const char *restrict src);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ false, @@ -1470,9 +1477,6 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // char *strncpy(char *restrict dst, const char *restrict src, size_t n);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ false, @@ -1481,9 +1485,6 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    // char *stpcpy(char *restrict dst, const char *restrict src);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ true, @@ -1492,9 +1493,6 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    // char *strlcpy(char *dst, const char *src, size_t n);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ true, @@ -1504,9 +1502,6 @@ void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    //char *strcat(char *restrict s1, const char *restrict s2);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ false, @@ -1515,9 +1510,6 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    //char *strncat(char *restrict s1, const char *restrict s2, size_t n);    evalStrcpyCommon(C, CE,                     /* returnEnd = */ false, @@ -1526,8 +1518,9 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; +  // FIXME: strlcat() uses a different rule for bound checking, i.e. 'n' means +  // a different thing as compared to strncat(). This currently causes +  // false positives in the alpha string bound checker.    //char *strlcat(char *s1, const char *s2, size_t n);    evalStrcpyCommon(C, CE, @@ -1881,35 +1874,23 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,  }  void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    //int strcmp(const char *s1, const char *s2);    evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false);  }  void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    //int strncmp(const char *s1, const char *s2, size_t n);    evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);  }  void CStringChecker::evalStrcasecmp(CheckerContext &C,      const CallExpr *CE) const { -  if (CE->getNumArgs() < 2) -    return; -    //int strcasecmp(const char *s1, const char *s2);    evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);  }  void CStringChecker::evalStrncasecmp(CheckerContext &C,      const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) -    return; -    //int strncasecmp(const char *s1, const char *s2, size_t n);    evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true);  } @@ -2043,9 +2024,6 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,  void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {    //char *strsep(char **stringp, const char *delim); -  if (CE->getNumArgs() < 2) -    return; -    // Sanity: does the search string parameter match the return type?    const Expr *SearchStrPtr = CE->getArg(0);    QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType(); @@ -2114,7 +2092,7 @@ void CStringChecker::evalStdCopyBackward(CheckerContext &C,  void CStringChecker::evalStdCopyCommon(CheckerContext &C,      const CallExpr *CE) const { -  if (CE->getNumArgs() < 3) +  if (!CE->getArg(2)->getType()->isPointerType())      return;    ProgramStateRef State = C.getState(); @@ -2141,9 +2119,6 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,  }  void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() != 3) -    return; -    CurrentFunctionDescription = "memory set function";    const Expr *Mem = CE->getArg(0); @@ -2192,9 +2167,6 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {  }  void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { -  if (CE->getNumArgs() != 2) -    return; -    CurrentFunctionDescription = "memory clearance function";    const Expr *Mem = CE->getArg(0); @@ -2237,110 +2209,53 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {    C.addTransition(State);  } -static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { -  IdentifierInfo *II = FD->getIdentifier(); -  if (!II) -    return false; - -  if (!AnalysisDeclContext::isInStdNamespace(FD)) -    return false; - -  if (II->getName().equals(Name)) -    return true; - -  return false; -}  //===----------------------------------------------------------------------===//  // The driver method, and other Checker callbacks.  //===----------------------------------------------------------------------===// -static CStringChecker::FnCheck identifyCall(const CallExpr *CE, -                                            CheckerContext &C) { -  const FunctionDecl *FDecl = C.getCalleeDecl(CE); -  if (!FDecl) +CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call, +                                                     CheckerContext &C) const { +  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); +  if (!CE) +    return nullptr; + +  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); +  if (!FD)      return nullptr; +  if (Call.isCalled(StdCopy)) { +    return &CStringChecker::evalStdCopy; +  } else if (Call.isCalled(StdCopyBackward)) { +    return &CStringChecker::evalStdCopyBackward; +  } +    // Pro-actively check that argument types are safe to do arithmetic upon.    // We do not want to crash if someone accidentally passes a structure -  // into, say, a C++ overload of any of these functions. -  if (isCPPStdLibraryFunction(FDecl, "copy")) { -    if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) +  // into, say, a C++ overload of any of these functions. We could not check +  // that for std::copy because they may have arguments of other types. +  for (auto I : CE->arguments()) { +    QualType T = I->getType(); +    if (!T->isIntegralOrEnumerationType() && !T->isPointerType())        return nullptr; -    return &CStringChecker::evalStdCopy; -  } else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) { -    if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) -      return nullptr; -    return &CStringChecker::evalStdCopyBackward; -  } else { -    // An umbrella check for all C library functions. -    for (auto I: CE->arguments()) { -      QualType T = I->getType(); -      if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) -        return nullptr; -    }    } -  // FIXME: Poorly-factored string switches are slow. -  if (C.isCLibraryFunction(FDecl, "memcpy")) -    return &CStringChecker::evalMemcpy; -  else if (C.isCLibraryFunction(FDecl, "mempcpy")) -    return &CStringChecker::evalMempcpy; -  else if (C.isCLibraryFunction(FDecl, "memcmp")) -    return &CStringChecker::evalMemcmp; -  else if (C.isCLibraryFunction(FDecl, "memmove")) -    return &CStringChecker::evalMemmove; -  else if (C.isCLibraryFunction(FDecl, "memset") || -           C.isCLibraryFunction(FDecl, "explicit_memset")) -    return &CStringChecker::evalMemset; -  else if (C.isCLibraryFunction(FDecl, "strcpy")) -    return &CStringChecker::evalStrcpy; -  else if (C.isCLibraryFunction(FDecl, "strncpy")) -    return &CStringChecker::evalStrncpy; -  else if (C.isCLibraryFunction(FDecl, "stpcpy")) -    return &CStringChecker::evalStpcpy; -  else if (C.isCLibraryFunction(FDecl, "strlcpy")) -    return &CStringChecker::evalStrlcpy; -  else if (C.isCLibraryFunction(FDecl, "strcat")) -    return &CStringChecker::evalStrcat; -  else if (C.isCLibraryFunction(FDecl, "strncat")) -    return &CStringChecker::evalStrncat; -  else if (C.isCLibraryFunction(FDecl, "strlcat")) -    return &CStringChecker::evalStrlcat; -  else if (C.isCLibraryFunction(FDecl, "strlen")) -    return &CStringChecker::evalstrLength; -  else if (C.isCLibraryFunction(FDecl, "strnlen")) -    return &CStringChecker::evalstrnLength; -  else if (C.isCLibraryFunction(FDecl, "strcmp")) -    return &CStringChecker::evalStrcmp; -  else if (C.isCLibraryFunction(FDecl, "strncmp")) -    return &CStringChecker::evalStrncmp; -  else if (C.isCLibraryFunction(FDecl, "strcasecmp")) -    return &CStringChecker::evalStrcasecmp; -  else if (C.isCLibraryFunction(FDecl, "strncasecmp")) -    return &CStringChecker::evalStrncasecmp; -  else if (C.isCLibraryFunction(FDecl, "strsep")) -    return &CStringChecker::evalStrsep; -  else if (C.isCLibraryFunction(FDecl, "bcopy")) -    return &CStringChecker::evalBcopy; -  else if (C.isCLibraryFunction(FDecl, "bcmp")) -    return &CStringChecker::evalMemcmp; -  else if (C.isCLibraryFunction(FDecl, "bzero") || -           C.isCLibraryFunction(FDecl, "explicit_bzero")) -    return &CStringChecker::evalBzero; +  const FnCheck *Callback = Callbacks.lookup(Call); +  if (Callback) +    return *Callback;    return nullptr;  } -bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - -  FnCheck evalFunction = identifyCall(CE, C); +bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { +  FnCheck Callback = identifyCall(Call, C);    // If the callee isn't a string function, let another checker handle it. -  if (!evalFunction) +  if (!Callback)      return false;    // Check and evaluate the call. -  (this->*evalFunction)(C, CE); +  const auto *CE = cast<CallExpr>(Call.getOriginExpr()); +  (this->*Callback)(C, CE);    // If the evaluate call resulted in no change, chain to the next eval call    // handler. @@ -2476,18 +2391,26 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,    C.addTransition(state);  } +void ento::registerCStringModeling(CheckerManager &Mgr) { +  Mgr.registerChecker<CStringChecker>(); +} + +bool ento::shouldRegisterCStringModeling(const LangOptions &LO) { +  return true; +} +  #define REGISTER_CHECKER(name)                                                 \    void ento::register##name(CheckerManager &mgr) {                             \ -    CStringChecker *checker = mgr.registerChecker<CStringChecker>();           \ +    CStringChecker *checker = mgr.getChecker<CStringChecker>();                \      checker->Filter.Check##name = true;                                        \      checker->Filter.CheckName##name = mgr.getCurrentCheckName();               \ +  }                                                                            \ +                                                                               \ +  bool ento::shouldRegister##name(const LangOptions &LO) {                     \ +    return true;                                                               \    }    REGISTER_CHECKER(CStringNullArg)    REGISTER_CHECKER(CStringOutOfBounds)    REGISTER_CHECKER(CStringBufferOverlap)  REGISTER_CHECKER(CStringNotNullTerm) - -  void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { -    Mgr.registerChecker<CStringChecker>(); -  }  | 
