diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp')
| -rw-r--r-- | lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp | 124 | 
1 files changed, 115 insertions, 9 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 163ca9d8556f9..3f1c213a56478 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -1,9 +1,8 @@  //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- 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  //  //===----------------------------------------------------------------------===//  // @@ -45,6 +44,7 @@ struct ChecksFilter {    DefaultBool check_mktemp;    DefaultBool check_mkstemp;    DefaultBool check_strcpy; +  DefaultBool check_DeprecatedOrUnsafeBufferHandling;    DefaultBool check_rand;    DefaultBool check_vfork;    DefaultBool check_FloatLoopCounter; @@ -58,6 +58,7 @@ struct ChecksFilter {    CheckName checkName_mktemp;    CheckName checkName_mkstemp;    CheckName checkName_strcpy; +  CheckName checkName_DeprecatedOrUnsafeBufferHandling;    CheckName checkName_rand;    CheckName checkName_vfork;    CheckName checkName_FloatLoopCounter; @@ -104,6 +105,8 @@ public:    void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);    void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);    void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); +  void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, +                                             const FunctionDecl *FD);    void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);    void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);    void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); @@ -149,6 +152,14 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {      .Case("mkstemps", &WalkAST::checkCall_mkstemp)      .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)      .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) +    .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", +           "vscanf", "vwscanf", "vfscanf", "vfwscanf", +           &WalkAST::checkDeprecatedOrUnsafeBufferHandling) +    .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", +           "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", +           &WalkAST::checkDeprecatedOrUnsafeBufferHandling) +    .Cases("strncpy", "strncat", "memset", +           &WalkAST::checkDeprecatedOrUnsafeBufferHandling)      .Case("drand48", &WalkAST::checkCall_rand)      .Case("erand48", &WalkAST::checkCall_rand)      .Case("jrand48", &WalkAST::checkCall_rand) @@ -553,7 +564,6 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {                       CELoc, CE->getCallee()->getSourceRange());  } -  //===----------------------------------------------------------------------===//  // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.  //===----------------------------------------------------------------------===// @@ -642,6 +652,7 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {  // CWE-119: Improper Restriction of Operations within  // the Bounds of a Memory Buffer  //===----------------------------------------------------------------------===// +  void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {    if (!filter.check_strcpy)      return; @@ -680,6 +691,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {  // CWE-119: Improper Restriction of Operations within  // the Bounds of a Memory Buffer  //===----------------------------------------------------------------------===// +  void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {    if (!filter.check_strcpy)      return; @@ -702,8 +714,92 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {  }  //===----------------------------------------------------------------------===// +// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', +//        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', +//        'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', +//        'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' +//        is deprecated since C11. +// +//        Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', +//        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', +//        'swscanf', 'vsscanf', 'vswscanf' without buffer limitations +//        is insecure. +// +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer +//===----------------------------------------------------------------------===// + +void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, +                                                    const FunctionDecl *FD) { +  if (!filter.check_DeprecatedOrUnsafeBufferHandling) +    return; + +  if (!BR.getContext().getLangOpts().C11) +    return; + +  // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size +  // restrictions). +  enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; + +  StringRef Name = FD->getIdentifier()->getName(); +  if (Name.startswith("__builtin_")) +    Name = Name.substr(10); + +  int ArgIndex = +      llvm::StringSwitch<int>(Name) +          .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) +          .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", +                 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) +          .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", +                 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) +          .Default(UNKNOWN_CALL); + +  assert(ArgIndex != UNKNOWN_CALL && "Unsupported function"); +  bool BoundsProvided = ArgIndex == DEPR_ONLY; + +  if (!BoundsProvided) { +    // Currently we only handle (not wide) string literals. It is possible to do +    // better, either by looking at references to const variables, or by doing +    // real flow analysis. +    auto FormatString = +        dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); +    if (FormatString && +        FormatString->getString().find("%s") == StringRef::npos && +        FormatString->getString().find("%[") == StringRef::npos) +      BoundsProvided = true; +  } + +  SmallString<128> Buf1; +  SmallString<512> Buf2; +  llvm::raw_svector_ostream Out1(Buf1); +  llvm::raw_svector_ostream Out2(Buf2); + +  Out1 << "Potential insecure memory buffer bounds restriction in call '" +       << Name << "'"; +  Out2 << "Call to function '" << Name +       << "' is insecure as it does not provide "; + +  if (!BoundsProvided) { +    Out2 << "bounding of the memory buffer or "; +  } + +  Out2 << "security checks introduced " +          "in the C11 standard. Replace with analogous functions that " +          "support length arguments or provides boundary checks such as '" +       << Name << "_s' in case of C11"; + +  PathDiagnosticLocation CELoc = +      PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); +  BR.EmitBasicReport(AC->getDecl(), +                     filter.checkName_DeprecatedOrUnsafeBufferHandling, +                     Out1.str(), "Security", Out2.str(), CELoc, +                     CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===//  // Common check for str* functions with no bounds parameters.  //===----------------------------------------------------------------------===// +  bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {    const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();    if (!FPT) @@ -906,12 +1002,23 @@ public:  };  } +void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { +  mgr.registerChecker<SecuritySyntaxChecker>(); +} + +bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { +  return true; +} +  #define REGISTER_CHECKER(name)                                                 \    void ento::register##name(CheckerManager &mgr) {                             \ -    SecuritySyntaxChecker *checker =                                           \ -        mgr.registerChecker<SecuritySyntaxChecker>();                          \ +    SecuritySyntaxChecker *checker =  mgr.getChecker<SecuritySyntaxChecker>(); \      checker->filter.check_##name = true;                                       \      checker->filter.checkName_##name = mgr.getCurrentCheckName();              \ +  }                                                                            \ +                                                                               \ +  bool ento::shouldRegister##name(const LangOptions &LO) {                     \ +    return true;                                                               \    }  REGISTER_CHECKER(bcmp) @@ -926,5 +1033,4 @@ REGISTER_CHECKER(rand)  REGISTER_CHECKER(vfork)  REGISTER_CHECKER(FloatLoopCounter)  REGISTER_CHECKER(UncheckedReturn) - - +REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)  | 
