brintos

brintos / llvm-project-archived public Read only

0
0
Text · 38.4 KiB · 7198c1b Raw
1013 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "NotNullTerminatedResultCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Frontend/CompilerInstance.h"13#include "clang/Lex/Lexer.h"14#include "clang/Lex/PPCallbacks.h"15#include "clang/Lex/Preprocessor.h"16#include <optional>17 18using namespace clang::ast_matchers;19 20namespace clang::tidy::bugprone {21 22constexpr llvm::StringLiteral FunctionExprName = "FunctionExpr";23constexpr llvm::StringLiteral CastExprName = "CastExpr";24constexpr llvm::StringLiteral UnknownDestName = "UnknownDest";25constexpr llvm::StringLiteral DestArrayTyName = "DestArrayTy";26constexpr llvm::StringLiteral DestVarDeclName = "DestVarDecl";27constexpr llvm::StringLiteral DestMallocExprName = "DestMalloc";28constexpr llvm::StringLiteral DestExprName = "DestExpr";29constexpr llvm::StringLiteral SrcVarDeclName = "SrcVarDecl";30constexpr llvm::StringLiteral SrcExprName = "SrcExpr";31constexpr llvm::StringLiteral LengthExprName = "LengthExpr";32constexpr llvm::StringLiteral WrongLengthExprName = "WrongLength";33constexpr llvm::StringLiteral UnknownLengthName = "UnknownLength";34 35enum class LengthHandleKind { Increase, Decrease };36 37namespace {38static Preprocessor *PP;39} // namespace40 41// Returns the expression of destination's capacity which is part of a42// 'VariableArrayType', 'ConstantArrayTypeLoc' or an argument of a 'malloc()'43// family function call.44static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) {45  if (const auto *DestMalloc = Result.Nodes.getNodeAs<Expr>(DestMallocExprName))46    return DestMalloc;47 48  if (const auto *DestVAT =49          Result.Nodes.getNodeAs<VariableArrayType>(DestArrayTyName))50    return DestVAT->getSizeExpr();51 52  if (const auto *DestVD = Result.Nodes.getNodeAs<VarDecl>(DestVarDeclName))53    if (const TypeLoc DestTL = DestVD->getTypeSourceInfo()->getTypeLoc())54      if (const auto DestCTL = DestTL.getAs<ConstantArrayTypeLoc>())55        return DestCTL.getSizeExpr();56 57  return nullptr;58}59 60// Returns the length of \p E as an 'IntegerLiteral' or a 'StringLiteral'61// without the null-terminator.62static unsigned getLength(const Expr *E,63                          const MatchFinder::MatchResult &Result) {64  if (!E)65    return 0;66 67  E = E->IgnoreImpCasts();68 69  if (const auto *LengthDRE = dyn_cast<DeclRefExpr>(E))70    if (const auto *LengthVD = dyn_cast<VarDecl>(LengthDRE->getDecl()))71      if (!isa<ParmVarDecl>(LengthVD))72        if (const Expr *LengthInit = LengthVD->getInit();73            LengthInit && !LengthInit->isValueDependent()) {74          Expr::EvalResult Length;75          if (LengthInit->EvaluateAsInt(Length, *Result.Context))76            return Length.Val.getInt().getZExtValue();77        }78 79  if (const auto *LengthIL = dyn_cast<IntegerLiteral>(E))80    return LengthIL->getValue().getZExtValue();81 82  if (const auto *StrDRE = dyn_cast<DeclRefExpr>(E))83    if (const auto *StrVD = dyn_cast<VarDecl>(StrDRE->getDecl()))84      if (const Expr *StrInit = StrVD->getInit())85        if (const auto *StrSL =86                dyn_cast<StringLiteral>(StrInit->IgnoreImpCasts()))87          return StrSL->getLength();88 89  if (const auto *SrcSL = dyn_cast<StringLiteral>(E))90    return SrcSL->getLength();91 92  return 0;93}94 95// Returns the capacity of the destination array.96// For example in 'char dest[13]; memcpy(dest, ...)' it returns 13.97static int getDestCapacity(const MatchFinder::MatchResult &Result) {98  if (const auto *DestCapacityExpr = getDestCapacityExpr(Result))99    return getLength(DestCapacityExpr, Result);100 101  return 0;102}103 104// Returns the 'strlen()' if it is the given length.105static const CallExpr *getStrlenExpr(const MatchFinder::MatchResult &Result) {106  if (const auto *StrlenExpr =107          Result.Nodes.getNodeAs<CallExpr>(WrongLengthExprName))108    if (const Decl *D = StrlenExpr->getCalleeDecl())109      if (const FunctionDecl *FD = D->getAsFunction())110        if (const IdentifierInfo *II = FD->getIdentifier())111          if (II->isStr("strlen") || II->isStr("wcslen"))112            return StrlenExpr;113 114  return nullptr;115}116 117// Returns the length which is given in the memory/string handler function.118// For example in 'memcpy(dest, "foobar", 3)' it returns 3.119static int getGivenLength(const MatchFinder::MatchResult &Result) {120  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))121    return 0;122 123  if (const int Length =124          getLength(Result.Nodes.getNodeAs<Expr>(WrongLengthExprName), Result))125    return Length;126 127  if (const int Length =128          getLength(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result))129    return Length;130 131  // Special case, for example 'strlen("foo")'.132  if (const CallExpr *StrlenCE = getStrlenExpr(Result))133    if (const Expr *Arg = StrlenCE->getArg(0)->IgnoreImpCasts())134      if (const int ArgLength = getLength(Arg, Result))135        return ArgLength;136 137  return 0;138}139 140// Returns a string representation of \p E.141static StringRef exprToStr(const Expr *E,142                           const MatchFinder::MatchResult &Result) {143  if (!E)144    return "";145 146  return Lexer::getSourceText(147      CharSourceRange::getTokenRange(E->getSourceRange()),148      *Result.SourceManager, Result.Context->getLangOpts(), nullptr);149}150 151// Returns the proper token based end location of \p E.152static SourceLocation exprLocEnd(const Expr *E,153                                 const MatchFinder::MatchResult &Result) {154  return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager,155                                    Result.Context->getLangOpts());156}157 158//===----------------------------------------------------------------------===//159// Rewrite decision helper functions.160//===----------------------------------------------------------------------===//161 162// Increment by integer '1' can result in overflow if it is the maximal value.163// After that it would be extended to 'size_t' and its value would be wrong,164// therefore we have to inject '+ 1UL' instead.165static bool isInjectUL(const MatchFinder::MatchResult &Result) {166  return getGivenLength(Result) == std::numeric_limits<int>::max();167}168 169// If the capacity of the destination array is unknown it is denoted as unknown.170static bool isKnownDest(const MatchFinder::MatchResult &Result) {171  return !Result.Nodes.getNodeAs<Expr>(UnknownDestName);172}173 174// True if the capacity of the destination array is based on the given length,175// therefore we assume that it cannot overflow (e.g. 'malloc(given_length + 1)'176static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) {177  const StringRef DestCapacityExprStr =178      exprToStr(getDestCapacityExpr(Result), Result).trim();179  const StringRef LengthExprStr =180      exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result).trim();181 182  return !DestCapacityExprStr.empty() && !LengthExprStr.empty() &&183         DestCapacityExprStr.contains(LengthExprStr);184}185 186// Writing and reading from the same memory cannot remove the null-terminator.187static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) {188  if (const auto *DestDRE = Result.Nodes.getNodeAs<DeclRefExpr>(DestExprName))189    if (const auto *SrcDRE = Result.Nodes.getNodeAs<DeclRefExpr>(SrcExprName))190      return DestDRE->getDecl()->getCanonicalDecl() ==191             SrcDRE->getDecl()->getCanonicalDecl();192 193  return false;194}195 196// For example 'std::string str = "foo"; memcpy(dst, str.data(), str.length())'.197static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) {198  const auto *DestExpr =199      Result.Nodes.getNodeAs<CXXMemberCallExpr>(DestExprName);200  const auto *SrcExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(SrcExprName);201  const auto *LengthExpr =202      Result.Nodes.getNodeAs<CXXMemberCallExpr>(WrongLengthExprName);203 204  StringRef DestStr = "", SrcStr = "", LengthStr = "";205  if (DestExpr)206    if (const CXXMethodDecl *DestMD = DestExpr->getMethodDecl())207      DestStr = DestMD->getName();208 209  if (SrcExpr)210    if (const CXXMethodDecl *SrcMD = SrcExpr->getMethodDecl())211      SrcStr = SrcMD->getName();212 213  if (LengthExpr)214    if (const CXXMethodDecl *LengthMD = LengthExpr->getMethodDecl())215      LengthStr = LengthMD->getName();216 217  return (LengthStr == "length" || LengthStr == "size") &&218         (SrcStr == "data" || DestStr == "data");219}220 221static bool222isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) {223  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))224    return false;225 226  if (isStringDataAndLength(Result))227    return true;228 229  const int GivenLength = getGivenLength(Result);230  const int SrcLength =231      getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);232 233  if (GivenLength != 0 && SrcLength != 0 && GivenLength == SrcLength)234    return true;235 236  if (const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName))237    if (isa<BinaryOperator>(LengthExpr->IgnoreParenImpCasts()))238      return false;239 240  // Check the strlen()'s argument's 'VarDecl' is equal to the source 'VarDecl'.241  if (const CallExpr *StrlenCE = getStrlenExpr(Result))242    if (const auto *ArgDRE =243            dyn_cast<DeclRefExpr>(StrlenCE->getArg(0)->IgnoreImpCasts()))244      if (const auto *SrcVD = Result.Nodes.getNodeAs<VarDecl>(SrcVarDeclName))245        return dyn_cast<VarDecl>(ArgDRE->getDecl()) == SrcVD;246 247  return false;248}249 250static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) {251  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))252    return false;253 254  return !isGivenLengthEqualToSrcLength(Result);255}256 257// If we rewrite the function call we need to create extra space to hold the258// null terminator. The new necessary capacity overflows without that '+ 1'259// size and we need to correct the given capacity.260static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) {261  if (!isKnownDest(Result))262    return true;263 264  const Expr *DestCapacityExpr = getDestCapacityExpr(Result);265  const int DestCapacity = getLength(DestCapacityExpr, Result);266  const int GivenLength = getGivenLength(Result);267 268  if (GivenLength != 0 && DestCapacity != 0)269    return isGivenLengthEqualToSrcLength(Result) && DestCapacity == GivenLength;270 271  // Assume that the destination array's capacity cannot overflow if the272  // expression of the memory allocation contains '+ 1'.273  const StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result);274  if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1"))275    return false;276 277  return true;278}279 280static bool281isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result) {282  if (Result.Nodes.getNodeAs<IntegerLiteral>(WrongLengthExprName))283    return !getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);284 285  return false;286}287 288//===----------------------------------------------------------------------===//289// Code injection functions.290//===----------------------------------------------------------------------===//291 292// Increase or decrease \p LengthExpr by one.293static void lengthExprHandle(const Expr *LengthExpr,294                             LengthHandleKind LengthHandle,295                             const MatchFinder::MatchResult &Result,296                             DiagnosticBuilder &Diag) {297  LengthExpr = LengthExpr->IgnoreParenImpCasts();298 299  // See whether we work with a macro.300  bool IsMacroDefinition = false;301  const StringRef LengthExprStr = exprToStr(LengthExpr, Result);302  Preprocessor::macro_iterator It = PP->macro_begin();303  while (It != PP->macro_end() && !IsMacroDefinition) {304    if (It->first->getName() == LengthExprStr)305      IsMacroDefinition = true;306 307    ++It;308  }309 310  // Try to obtain an 'IntegerLiteral' and adjust it.311  if (!IsMacroDefinition) {312    if (const auto *LengthIL = dyn_cast<IntegerLiteral>(LengthExpr)) {313      const uint64_t NewLength =314          LengthIL->getValue().getZExtValue() +315          (LengthHandle == LengthHandleKind::Increase ? 1 : -1);316 317      const auto NewLengthFix = FixItHint::CreateReplacement(318          LengthIL->getSourceRange(),319          (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str());320      Diag << NewLengthFix;321      return;322    }323  }324 325  // Try to obtain and remove the '+ 1' string as a decrement fix.326  const auto *BO = dyn_cast<BinaryOperator>(LengthExpr);327  if (BO && BO->getOpcode() == BO_Add &&328      LengthHandle == LengthHandleKind::Decrease) {329    const Expr *LhsExpr = BO->getLHS()->IgnoreImpCasts();330    const Expr *RhsExpr = BO->getRHS()->IgnoreImpCasts();331 332    if (const auto *LhsIL = dyn_cast<IntegerLiteral>(LhsExpr)) {333      if (LhsIL->getValue().getZExtValue() == 1) {334        Diag << FixItHint::CreateRemoval(335            {LhsIL->getBeginLoc(),336             RhsExpr->getBeginLoc().getLocWithOffset(-1)});337        return;338      }339    }340 341    if (const auto *RhsIL = dyn_cast<IntegerLiteral>(RhsExpr)) {342      if (RhsIL->getValue().getZExtValue() == 1) {343        Diag << FixItHint::CreateRemoval(344            {LhsExpr->getEndLoc().getLocWithOffset(1), RhsIL->getEndLoc()});345        return;346      }347    }348  }349 350  // Try to inject the '+ 1'/'- 1' string.351  const bool NeedInnerParen = BO && BO->getOpcode() != BO_Add;352 353  if (NeedInnerParen)354    Diag << FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "(");355 356  SmallString<8> Injection;357  if (NeedInnerParen)358    Injection += ')';359  Injection += LengthHandle == LengthHandleKind::Increase ? " + 1" : " - 1";360  if (isInjectUL(Result))361    Injection += "UL";362 363  Diag << FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), Injection);364}365 366static void lengthArgHandle(LengthHandleKind LengthHandle,367                            const MatchFinder::MatchResult &Result,368                            DiagnosticBuilder &Diag) {369  const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName);370  lengthExprHandle(LengthExpr, LengthHandle, Result, Diag);371}372 373static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle,374                               const MatchFinder::MatchResult &Result,375                               DiagnosticBuilder &Diag) {376  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);377  lengthExprHandle(FunctionExpr->getArg(ArgPos), LengthHandle, Result, Diag);378}379 380// The string handler functions are only operates with plain 'char'/'wchar_t'381// without 'unsigned/signed', therefore we need to cast it.382static bool isDestExprFix(const MatchFinder::MatchResult &Result,383                          DiagnosticBuilder &Diag) {384  const auto *Dest = Result.Nodes.getNodeAs<Expr>(DestExprName);385  if (!Dest)386    return false;387 388  const std::string TempTyStr = Dest->getType().getAsString();389  const StringRef TyStr = TempTyStr;390  if (TyStr.starts_with("char") || TyStr.starts_with("wchar_t"))391    return false;392 393  Diag << FixItHint::CreateInsertion(Dest->getBeginLoc(), "(char *)");394  return true;395}396 397// If the destination array is the same length as the given length we have to398// increase the capacity by one to create space for the null terminator.399static bool isDestCapacityFix(const MatchFinder::MatchResult &Result,400                              DiagnosticBuilder &Diag) {401  const bool IsOverflows = isDestCapacityOverflows(Result);402  if (IsOverflows)403    if (const Expr *CapacityExpr = getDestCapacityExpr(Result))404      lengthExprHandle(CapacityExpr, LengthHandleKind::Increase, Result, Diag);405 406  return IsOverflows;407}408 409static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result,410                      DiagnosticBuilder &Diag) {411  // This is the following structure: (src, '\0', strlen(src))412  //                     ArgToRemove:             ~~~~~~~~~~~413  //                          LHSArg:       ~~~~414  //                    RemoveArgFix:           ~~~~~~~~~~~~~415  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);416  const Expr *ArgToRemove = FunctionExpr->getArg(ArgPos);417  const Expr *LHSArg = FunctionExpr->getArg(ArgPos - 1);418  const auto RemoveArgFix = FixItHint::CreateRemoval(419      SourceRange(exprLocEnd(LHSArg, Result),420                  exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1)));421  Diag << RemoveArgFix;422}423 424static void renameFunc(StringRef NewFuncName,425                       const MatchFinder::MatchResult &Result,426                       DiagnosticBuilder &Diag) {427  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);428  const int FuncNameLength =429      FunctionExpr->getDirectCallee()->getIdentifier()->getLength();430  const SourceRange FuncNameRange(431      FunctionExpr->getBeginLoc(),432      FunctionExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1));433 434  const auto FuncNameFix =435      FixItHint::CreateReplacement(FuncNameRange, NewFuncName);436  Diag << FuncNameFix;437}438 439static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe,440                         const MatchFinder::MatchResult &Result,441                         DiagnosticBuilder &Diag) {442  SmallString<10> NewFuncName;443  NewFuncName = (Name[0] != 'w') ? "str" : "wcs";444  NewFuncName += IsCopy ? "cpy" : "ncpy";445  NewFuncName += IsSafe ? "_s" : "";446  renameFunc(NewFuncName, Result, Diag);447}448 449static void insertDestCapacityArg(bool IsOverflows, StringRef Name,450                                  const MatchFinder::MatchResult &Result,451                                  DiagnosticBuilder &Diag) {452  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);453  SmallString<64> NewSecondArg;454 455  if (const int DestLength = getDestCapacity(Result)) {456    NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str();457  } else {458    NewSecondArg =459        (Twine(exprToStr(getDestCapacityExpr(Result), Result)) +460         (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : ""))461            .str();462  }463 464  NewSecondArg += ", ";465  const auto InsertNewArgFix = FixItHint::CreateInsertion(466      FunctionExpr->getArg(1)->getBeginLoc(), NewSecondArg);467  Diag << InsertNewArgFix;468}469 470static void insertNullTerminatorExpr(StringRef Name,471                                     const MatchFinder::MatchResult &Result,472                                     DiagnosticBuilder &Diag) {473  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);474  const int FuncLocStartColumn = Result.SourceManager->getPresumedColumnNumber(475      FunctionExpr->getBeginLoc());476  const SourceRange SpaceRange(477      FunctionExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1),478      FunctionExpr->getBeginLoc());479  const StringRef SpaceBeforeStmtStr = Lexer::getSourceText(480      CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager,481      Result.Context->getLangOpts(), nullptr);482 483  SmallString<128> NewAddNullTermExprStr;484  NewAddNullTermExprStr =485      (Twine('\n') + SpaceBeforeStmtStr +486       exprToStr(Result.Nodes.getNodeAs<Expr>(DestExprName), Result) + "[" +487       exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result) +488       "] = " + ((Name[0] != 'w') ? R"('\0';)" : R"(L'\0';)"))489          .str();490 491  const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion(492      exprLocEnd(FunctionExpr, Result).getLocWithOffset(1),493      NewAddNullTermExprStr);494  Diag << AddNullTerminatorExprFix;495}496 497//===----------------------------------------------------------------------===//498// Checker logic with the matchers.499//===----------------------------------------------------------------------===//500 501NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(502    StringRef Name, ClangTidyContext *Context)503    : ClangTidyCheck(Name, Context),504      WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", true)) {}505 506void NotNullTerminatedResultCheck::storeOptions(507    ClangTidyOptions::OptionMap &Opts) {508  Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions);509}510 511void NotNullTerminatedResultCheck::registerPPCallbacks(512    const SourceManager &SM, Preprocessor *Pp, Preprocessor *ModuleExpanderPP) {513  PP = Pp;514}515 516namespace {517AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>,518              InnerMatcher) {519  const Expr *SimpleNode = &Node;520  SimpleNode = SimpleNode->IgnoreParenImpCasts();521 522  if (InnerMatcher.matches(*SimpleNode, Finder, Builder))523    return true;524 525  auto DREHasInit = ignoringImpCasts(526      declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher))))));527 528  if (DREHasInit.matches(*SimpleNode, Finder, Builder))529    return true;530 531  const char *const VarDeclName = "variable-declaration";532  auto DREHasDefinition = ignoringImpCasts(declRefExpr(533      to(varDecl().bind(VarDeclName)),534      hasAncestor(compoundStmt(hasDescendant(binaryOperator(535          hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))),536          hasRHS(ignoringImpCasts(InnerMatcher))))))));537 538  if (DREHasDefinition.matches(*SimpleNode, Finder, Builder))539    return true;540 541  return false;542}543} // namespace544 545void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) {546  auto IncOp =547      binaryOperator(hasOperatorName("+"),548                     hasEitherOperand(ignoringParenImpCasts(integerLiteral())));549 550  auto DecOp =551      binaryOperator(hasOperatorName("-"),552                     hasEitherOperand(ignoringParenImpCasts(integerLiteral())));553 554  auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp));555  auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp));556 557  auto Container = ignoringImpCasts(cxxMemberCallExpr(hasDescendant(declRefExpr(558      hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(recordDecl(559          hasAnyName("::std::vector", "::std::list", "::std::deque"))))))))));560 561  auto StringTy = type(hasUnqualifiedDesugaredType(recordType(562      hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));563 564  auto AnyOfStringTy =565      anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy))));566 567  auto CharTyArray = hasType(qualType(hasCanonicalType(568      arrayType(hasElementType(isAnyCharacter())).bind(DestArrayTyName))));569 570  auto CharTyPointer = hasType(571      qualType(hasCanonicalType(pointerType(pointee(isAnyCharacter())))));572 573  auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer);574 575  //===--------------------------------------------------------------------===//576  // The following six cases match problematic length expressions.577  //===--------------------------------------------------------------------===//578 579  // - Example:  char src[] = "foo";       strlen(src);580  auto Strlen =581      callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen"))))582          .bind(WrongLengthExprName);583 584  // - Example:  std::string str = "foo";  str.size();585  auto SizeOrLength =586      cxxMemberCallExpr(on(expr(AnyOfStringTy).bind("Foo")),587                        has(memberExpr(member(hasAnyName("size", "length")))))588          .bind(WrongLengthExprName);589 590  // - Example:  char src[] = "foo";       sizeof(src);591  auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy)));592 593  auto WrongLength =594      ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen),595                             hasDescendant(SizeOrLength)));596 597  // - Example:  length = strlen(src);598  auto DREWithoutInc =599      ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength)))));600 601  auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength);602 603  // - Example:  int getLength(const char *str) { return strlen(str); }604  auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl(605      hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc))))))));606 607  // - Example:  int length = getLength(src);608  auto DREHasReturnWithoutInc = ignoringImpCasts(609      declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc)))));610 611  auto AnyOfWrongLengthInit =612      anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc,613            DREHasReturnWithoutInc);614 615  //===--------------------------------------------------------------------===//616  // The following five cases match the 'destination' array length's617  // expression which is used in 'memcpy()' and 'memmove()' matchers.618  //===--------------------------------------------------------------------===//619 620  // Note: Sometimes the size of char is explicitly written out.621  auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1)));622 623  auto MallocLengthExpr = allOf(624      callee(functionDecl(625          hasAnyName("::alloca", "::calloc", "malloc", "realloc"))),626      hasAnyArgument(allOf(unless(SizeExpr), expr().bind(DestMallocExprName))));627 628  // - Example:  (char *)malloc(length);629  auto DestMalloc = anyOf(callExpr(MallocLengthExpr),630                          hasDescendant(callExpr(MallocLengthExpr)));631 632  // - Example:  new char[length];633  auto DestCXXNewExpr = ignoringImpCasts(634      cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName))));635 636  auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr);637 638  // - Example:  char dest[13];  or  char dest[length];639  auto DestArrayTyDecl = declRefExpr(640      to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName),641               varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName))));642 643  // - Example:  foo[bar[baz]].qux; (or just ParmVarDecl)644  auto DestUnknownDecl =645      declRefExpr(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)),646                  expr().bind(UnknownDestName))647          .bind(DestExprName);648 649  auto AnyOfDestDecl = ignoringImpCasts(650      anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl,651                                      hasDescendant(DestArrayTyDecl))),652                  expr().bind(DestExprName)),653            anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl))));654 655  auto NullTerminatorExpr = binaryOperator(656      hasLHS(anyOf(hasDescendant(declRefExpr(to(varDecl(657                       equalsBoundNode(std::string(DestVarDeclName)))))),658                   hasDescendant(declRefExpr(659                       equalsBoundNode(std::string(DestExprName)))))),660      hasRHS(ignoringImpCasts(661          anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0))))));662 663  auto SrcDecl =664      declRefExpr(to(decl().bind(SrcVarDeclName)),665                  anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)),666                        expr().bind(SrcExprName)));667 668  auto AnyOfSrcDecl =669      ignoringImpCasts(anyOf(stringLiteral().bind(SrcExprName),670                             hasDescendant(stringLiteral().bind(SrcExprName)),671                             SrcDecl, hasDescendant(SrcDecl)));672 673  //===--------------------------------------------------------------------===//674  // Match the problematic function calls.675  //===--------------------------------------------------------------------===//676 677  struct CallContext {678    CallContext(StringRef Name, std::optional<unsigned> DestinationPos,679                std::optional<unsigned> SourcePos, unsigned LengthPos,680                bool WithIncrease)681        : Name(Name), DestinationPos(DestinationPos), SourcePos(SourcePos),682          LengthPos(LengthPos), WithIncrease(WithIncrease) {};683 684    StringRef Name;685    std::optional<unsigned> DestinationPos;686    std::optional<unsigned> SourcePos;687    unsigned LengthPos;688    bool WithIncrease;689  };690 691  auto MatchDestination = [=](CallContext CC) {692    return hasArgument(*CC.DestinationPos,693                       allOf(AnyOfDestDecl,694                             unless(hasAncestor(compoundStmt(695                                 hasDescendant(NullTerminatorExpr)))),696                             unless(Container)));697  };698 699  auto MatchSource = [=](CallContext CC) {700    return hasArgument(*CC.SourcePos, AnyOfSrcDecl);701  };702 703  auto MatchGivenLength = [=](CallContext CC) {704    return hasArgument(705        CC.LengthPos,706        allOf(707            anyOf(ignoringImpCasts(integerLiteral().bind(WrongLengthExprName)),708                  allOf(unless(hasDefinition(SizeOfCharExpr)),709                        allOf(CC.WithIncrease710                                  ? ignoringImpCasts(hasDefinition(HasIncOp))711                                  : ignoringImpCasts(712                                        allOf(unless(hasDefinition(HasIncOp)),713                                              hasDefinition(optionally(714                                                  binaryOperator().bind(715                                                      UnknownLengthName))))),716                              AnyOfWrongLengthInit))),717            expr().bind(LengthExprName)));718  };719 720  auto MatchCall = [=](CallContext CC) {721    const std::string CharHandlerFuncName = "::" + CC.Name.str();722 723    // Try to match with 'wchar_t' based function calls.724    const std::string WcharHandlerFuncName =725        "::" + (CC.Name.starts_with("mem") ? "w" + CC.Name.str()726                                           : "wcs" + CC.Name.substr(3).str());727 728    return allOf(callee(functionDecl(729                     hasAnyName(CharHandlerFuncName, WcharHandlerFuncName))),730                 MatchGivenLength(CC));731  };732 733  auto Match = [=](CallContext CC) {734    if (CC.DestinationPos && CC.SourcePos)735      return allOf(MatchCall(CC), MatchDestination(CC), MatchSource(CC));736 737    if (CC.DestinationPos && !CC.SourcePos)738      return allOf(MatchCall(CC), MatchDestination(CC),739                   hasArgument(*CC.DestinationPos, anything()));740 741    if (!CC.DestinationPos && CC.SourcePos)742      return allOf(MatchCall(CC), MatchSource(CC),743                   hasArgument(*CC.SourcePos, anything()));744 745    llvm_unreachable("Unhandled match");746  };747 748  // void *memcpy(void *dest, const void *src, size_t count)749  auto Memcpy = Match({"memcpy", 0, 1, 2, false});750 751  // errno_t memcpy_s(void *dest, size_t ds, const void *src, size_t count)752  auto MemcpyS = Match({"memcpy_s", 0, 2, 3, false});753 754  // void *memchr(const void *src, int c, size_t count)755  auto Memchr = Match({"memchr", std::nullopt, 0, 2, false});756 757  // void *memmove(void *dest, const void *src, size_t count)758  auto Memmove = Match({"memmove", 0, 1, 2, false});759 760  // errno_t memmove_s(void *dest, size_t ds, const void *src, size_t count)761  auto MemmoveS = Match({"memmove_s", 0, 2, 3, false});762 763  // int strncmp(const char *str1, const char *str2, size_t count);764  auto StrncmpRHS = Match({"strncmp", std::nullopt, 1, 2, true});765  auto StrncmpLHS = Match({"strncmp", std::nullopt, 0, 2, true});766 767  // size_t strxfrm(char *dest, const char *src, size_t count);768  auto Strxfrm = Match({"strxfrm", 0, 1, 2, false});769 770  // errno_t strerror_s(char *buffer, size_t bufferSize, int errnum);771  auto StrerrorS = Match({"strerror_s", 0, std::nullopt, 1, false});772 773  auto AnyOfMatchers = anyOf(Memcpy, MemcpyS, Memmove, MemmoveS, StrncmpRHS,774                             StrncmpLHS, Strxfrm, StrerrorS);775 776  Finder->addMatcher(callExpr(AnyOfMatchers).bind(FunctionExprName), this);777 778  // Need to remove the CastExpr from 'memchr()' as 'strchr()' returns 'char *'.779  Finder->addMatcher(780      callExpr(Memchr,781               unless(hasAncestor(castExpr(unless(implicitCastExpr())))))782          .bind(FunctionExprName),783      this);784  Finder->addMatcher(785      castExpr(allOf(unless(implicitCastExpr()),786                     has(callExpr(Memchr).bind(FunctionExprName))))787          .bind(CastExprName),788      this);789}790 791void NotNullTerminatedResultCheck::check(792    const MatchFinder::MatchResult &Result) {793  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);794  if (FunctionExpr->getBeginLoc().isMacroID())795    return;796 797  if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) {798    std::optional<bool> AreSafeFunctionsWanted;799 800    Preprocessor::macro_iterator It = PP->macro_begin();801    while (It != PP->macro_end() && !AreSafeFunctionsWanted) {802      if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") {803        const auto *MI = PP->getMacroInfo(It->first);804        // PP->getMacroInfo() returns nullptr if macro has no definition.805        if (MI) {806          const auto &T = MI->tokens().back();807          if (T.isLiteral() && T.getLiteralData()) {808            const StringRef ValueStr =809                StringRef(T.getLiteralData(), T.getLength());810            llvm::APInt IntValue;811            ValueStr.getAsInteger(10, IntValue);812            AreSafeFunctionsWanted = IntValue.getZExtValue();813          }814        }815      }816 817      ++It;818    }819 820    if (AreSafeFunctionsWanted)821      UseSafeFunctions = *AreSafeFunctionsWanted;822  }823 824  const StringRef Name = FunctionExpr->getDirectCallee()->getName();825  if (Name.starts_with("mem") || Name.starts_with("wmem"))826    memoryHandlerFunctionFix(Name, Result);827  else if (Name == "strerror_s")828    strerrorSFix(Result);829  else if (Name.ends_with("ncmp"))830    ncmpFix(Name, Result);831  else if (Name.ends_with("xfrm"))832    xfrmFix(Name, Result);833}834 835void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(836    StringRef Name, const MatchFinder::MatchResult &Result) {837  if (isCorrectGivenLength(Result))838    return;839 840  if (Name.ends_with("chr")) {841    memchrFix(Name, Result);842    return;843  }844 845  if ((Name.contains("cpy") || Name.contains("move")) &&846      (isDestAndSrcEquals(Result) || isFixedGivenLengthAndUnknownSrc(Result)))847    return;848 849  auto Diag =850      diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),851           "the result from calling '%0' is not null-terminated")852      << Name;853 854  if (Name.ends_with("cpy")) {855    memcpyFix(Name, Result, Diag);856  } else if (Name.ends_with("cpy_s")) {857    memcpySFix(Name, Result, Diag);858  } else if (Name.ends_with("move")) {859    memmoveFix(Name, Result, Diag);860  } else if (Name.ends_with("move_s")) {861    isDestCapacityFix(Result, Diag);862    lengthArgHandle(LengthHandleKind::Increase, Result, Diag);863  }864}865 866void NotNullTerminatedResultCheck::memcpyFix(867    StringRef Name, const MatchFinder::MatchResult &Result,868    DiagnosticBuilder &Diag) {869  const bool IsOverflows = isDestCapacityFix(Result, Diag);870  const bool IsDestFixed = isDestExprFix(Result, Diag);871 872  const bool IsCopy =873      isGivenLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result);874 875  const bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) &&876                      !isDestBasedOnGivenLength(Result);877 878  const bool IsDestLengthNotRequired =879      IsSafe && getLangOpts().CPlusPlus &&880      Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && !IsDestFixed;881 882  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);883 884  if (IsSafe && !IsDestLengthNotRequired)885    insertDestCapacityArg(IsOverflows, Name, Result, Diag);886 887  if (IsCopy)888    removeArg(2, Result, Diag);889 890  if (!IsCopy && !IsSafe)891    insertNullTerminatorExpr(Name, Result, Diag);892}893 894void NotNullTerminatedResultCheck::memcpySFix(895    StringRef Name, const MatchFinder::MatchResult &Result,896    DiagnosticBuilder &Diag) {897  const bool IsOverflows = isDestCapacityFix(Result, Diag);898  const bool IsDestFixed = isDestExprFix(Result, Diag);899 900  const bool RemoveDestLength =901      getLangOpts().CPlusPlus &&902      Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && !IsDestFixed;903  const bool IsCopy = isGivenLengthEqualToSrcLength(Result);904  const bool IsSafe = IsOverflows;905 906  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);907 908  if (!IsSafe || (IsSafe && RemoveDestLength))909    removeArg(1, Result, Diag);910  else if (IsOverflows && isKnownDest(Result))911    lengthArgPosHandle(1, LengthHandleKind::Increase, Result, Diag);912 913  if (IsCopy)914    removeArg(3, Result, Diag);915 916  if (!IsCopy && !IsSafe)917    insertNullTerminatorExpr(Name, Result, Diag);918}919 920void NotNullTerminatedResultCheck::memchrFix(921    StringRef Name, const MatchFinder::MatchResult &Result) {922  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);923  if (const auto *GivenCL = dyn_cast<CharacterLiteral>(FunctionExpr->getArg(1)))924    if (GivenCL->getValue() != 0)925      return;926 927  auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),928                   "the length is too short to include the null terminator");929 930  if (const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(CastExprName)) {931    const auto CastRemoveFix = FixItHint::CreateRemoval(932        SourceRange(CastExpr->getBeginLoc(),933                    FunctionExpr->getBeginLoc().getLocWithOffset(-1)));934    Diag << CastRemoveFix;935  }936 937  const StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr";938  renameFunc(NewFuncName, Result, Diag);939  removeArg(2, Result, Diag);940}941 942void NotNullTerminatedResultCheck::memmoveFix(943    StringRef Name, const MatchFinder::MatchResult &Result,944    DiagnosticBuilder &Diag) const {945  const bool IsOverflows = isDestCapacityFix(Result, Diag);946 947  if (UseSafeFunctions && isKnownDest(Result)) {948    renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag);949    insertDestCapacityArg(IsOverflows, Name, Result, Diag);950  }951 952  lengthArgHandle(LengthHandleKind::Increase, Result, Diag);953}954 955void NotNullTerminatedResultCheck::strerrorSFix(956    const MatchFinder::MatchResult &Result) {957  auto Diag =958      diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),959           "the result from calling 'strerror_s' is not null-terminated and "960           "missing the last character of the error message");961 962  isDestCapacityFix(Result, Diag);963  lengthArgHandle(LengthHandleKind::Increase, Result, Diag);964}965 966void NotNullTerminatedResultCheck::ncmpFix(967    StringRef Name, const MatchFinder::MatchResult &Result) {968  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);969  const Expr *FirstArgExpr = FunctionExpr->getArg(0)->IgnoreImpCasts();970  const Expr *SecondArgExpr = FunctionExpr->getArg(1)->IgnoreImpCasts();971  bool IsLengthTooLong = false;972 973  if (const CallExpr *StrlenExpr = getStrlenExpr(Result)) {974    const Expr *LengthExprArg = StrlenExpr->getArg(0);975    const StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim();976    const StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim();977    const StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim();978    IsLengthTooLong =979        LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr;980  } else {981    const int SrcLength =982        getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);983    const int GivenLength = getGivenLength(Result);984    if (SrcLength != 0 && GivenLength != 0)985      IsLengthTooLong = GivenLength > SrcLength;986  }987 988  if (!IsLengthTooLong && !isStringDataAndLength(Result))989    return;990 991  auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),992                   "comparison length is too long and might lead to a "993                   "buffer overflow");994 995  lengthArgHandle(LengthHandleKind::Decrease, Result, Diag);996}997 998void NotNullTerminatedResultCheck::xfrmFix(999    StringRef Name, const MatchFinder::MatchResult &Result) {1000  if (!isDestCapacityOverflows(Result))1001    return;1002 1003  auto Diag =1004      diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),1005           "the result from calling '%0' is not null-terminated")1006      << Name;1007 1008  isDestCapacityFix(Result, Diag);1009  lengthArgHandle(LengthHandleKind::Increase, Result, Diag);1010}1011 1012} // namespace clang::tidy::bugprone1013