[clang-tools-extra] r344374 - [clang-tidy] New checker for not null-terminated result caused by strlen(), size() or equal length

Jonas Toth via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 12 10:22:36 PDT 2018


Author: jonastoth
Date: Fri Oct 12 10:22:36 2018
New Revision: 344374

URL: http://llvm.org/viewvc/llvm-project?rev=344374&view=rev
Log:
[clang-tidy] New checker for not null-terminated result caused by strlen(), size() or equal length

New checker called bugprone-not-null-terminated-result. This check finds function calls where it is possible to cause a not null-terminated result. Usually the proper length of a string is strlen(src) + 1 or equal length of this expression, because the null terminator needs an extra space. Without the null terminator it can result in undefined behaviour when the string is read.

The following function calls are checked:
memcpy, wmemcpy, memcpy_s, wmemcpy_s, memchr, wmemchr, memmove, wmemmove, memmove_s, wmemmove_s, memset, wmemset, strerror_s, strncmp, wcsncmp, strxfrm, wcsxfrm

The following is a real-world example where the programmer forgot to increase the passed third argument, which is size_t length. That is why the length of the allocated memory is problematic too.

static char *StringCpy(const std::string &str) {
  char *result = reinterpret_cast<char *>(malloc(str.size()));
  memcpy(result, str.data(), str.size());
  return result;
}

After running the tool fix-it rewrites all the necessary code according to the given options. If it is necessary, the buffer size will be increased to hold the null terminator.

static char *StringCpy(const std::string &str) {
  char *result = reinterpret_cast<char *>(malloc(str.size() + 1));
  strcpy(result, str.data());
  return result;
}

Patch by Charusso.

Differential ID: https://reviews.llvm.org/D45050

Added:
    clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp
    clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp
    clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst

Modified: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp?rev=344374&r1=344373&r2=344374&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp Fri Oct 12 10:22:36 2018
@@ -30,6 +30,7 @@
 #include "MisplacedWideningCastCheck.h"
 #include "MoveForwardingReferenceCheck.h"
 #include "MultipleStatementMacroCheck.h"
+#include "NotNullTerminatedResultCheck.h"
 #include "ParentVirtualCallCheck.h"
 #include "SizeofContainerCheck.h"
 #include "SizeofExpressionCheck.h"
@@ -98,6 +99,8 @@ public:
         "bugprone-multiple-statement-macro");
     CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
         "bugprone-narrowing-conversions");
+    CheckFactories.registerCheck<NotNullTerminatedResultCheck>(
+        "bugprone-not-null-terminated-result");
     CheckFactories.registerCheck<ParentVirtualCallCheck>(
         "bugprone-parent-virtual-call");
     CheckFactories.registerCheck<SizeofContainerCheck>(

Modified: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt?rev=344374&r1=344373&r2=344374&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt Fri Oct 12 10:22:36 2018
@@ -21,6 +21,7 @@ add_clang_library(clangTidyBugproneModul
   MisplacedWideningCastCheck.cpp
   MoveForwardingReferenceCheck.cpp
   MultipleStatementMacroCheck.cpp
+  NotNullTerminatedResultCheck.cpp
   ParentVirtualCallCheck.cpp
   SizeofContainerCheck.cpp
   SizeofExpressionCheck.cpp

Added: clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp Fri Oct 12 10:22:36 2018
@@ -0,0 +1,1024 @@
+//===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NotNullTerminatedResultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PPCallbacks.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static const char *const FuncExprName = "entire-called-function-expr";
+static const char *const CastExprName = "cast-expr";
+static const char *const UnknownDestName = "destination-length-is-unknown";
+static const char *const NotJustCharTyName = "unsigned-or-signed-char";
+static const char *const DestArrayTyName = "destination-is-array-type";
+static const char *const DestVarDeclName = "destination-variable-declaration";
+static const char *const SrcVarDeclName = "source-variable-declaration";
+static const char *const UnknownLengthName = "given-length-is-unknown";
+static const char *const WrongLengthExprName = "strlen-or-size";
+static const char *const DestMallocExprName = "destination-malloc-expr";
+static const char *const DestExprName = "destination-decl-ref-expr";
+static const char *const SrcExprName = "source-expression-or-string-literal";
+static const char *const LengthExprName = "given-length-expression";
+
+enum class LengthHandleKind { Increase, Decrease };
+
+namespace {
+static Preprocessor *PP;
+} // namespace
+
+// The capacity: VariableArrayType, ConstantArrayType, argument of a 'malloc()'
+// family function or an argument of a custom memory allocation.
+static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result);
+
+static int getDestCapacity(const MatchFinder::MatchResult &Result);
+
+// Length could be an IntegerLiteral or length of a StringLiteral.
+static int getLength(const Expr *E, const MatchFinder::MatchResult &Result);
+
+static int getGivenLength(const MatchFinder::MatchResult &Result);
+
+static StringRef exprToStr(const Expr *E,
+                           const MatchFinder::MatchResult &Result);
+
+static SourceLocation exprLocEnd(const Expr *E,
+                                 const MatchFinder::MatchResult &Result) {
+  return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager,
+                                    Result.Context->getLangOpts());
+}
+
+//===----------------------------------------------------------------------===//
+// Rewrite decision helper functions.
+//===----------------------------------------------------------------------===//
+
+// Increment by integer '1' can result in overflow if it is the maximal value.
+// After that it will be extended to 'size_t' and its value will be wrong,
+// therefore we have to inject '+ 1UL' instead.
+static bool isInjectUL(const MatchFinder::MatchResult &Result) {
+  return getGivenLength(Result) == std::numeric_limits<int>::max();
+}
+
+// If the capacity of the destination array is unknown it is denoted as unknown.
+static bool isKnownDest(const MatchFinder::MatchResult &Result) {
+  return !Result.Nodes.getNodeAs<Expr>(UnknownDestName);
+}
+
+// True if the capacity of the destination array is based on the given length,
+// therefore it looks like it cannot overflow (e.g. 'malloc(given_length + 1)'
+// Note: If the capacity and the given length is equal then the new function
+// is a simple 'cpy()' and because it returns true it prevents increasing the
+// given length.
+static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result);
+
+// If we write/read from the same array it should be already null-terminated.
+static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result);
+
+// We catch integers as a given length so we have to see if the length of the
+// source array is the same length so that the function call is wrong.
+static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result);
+
+// Example:  memcpy(dest, str.data(), str.length());
+static bool isStringDataAndLength(const MatchFinder::MatchResult &Result);
+
+static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result);
+
+static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result);
+
+//===----------------------------------------------------------------------===//
+// Code injection functions.
+//===----------------------------------------------------------------------===//
+
+static void lengthDecrease(const Expr *LengthExpr,
+                           const MatchFinder::MatchResult &Result,
+                           DiagnosticBuilder &Diag);
+static void lengthIncrease(const Expr *LengthExpr,
+                           const MatchFinder::MatchResult &Result,
+                           DiagnosticBuilder &Diag);
+
+// Increase or decrease an integral expression by one.
+static void lengthExprHandle(LengthHandleKind LengthHandle,
+                             const Expr *LengthExpr,
+                             const MatchFinder::MatchResult &Result,
+                             DiagnosticBuilder &Diag);
+
+// Increase or decrease the passed integral argument by one.
+static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos,
+                            const MatchFinder::MatchResult &Result,
+                            DiagnosticBuilder &Diag);
+
+// If the destination array is the same length as the given length we have to
+// increase the capacity by one to create space for the the null terminator.
+static bool destCapacityFix(const MatchFinder::MatchResult &Result,
+                            DiagnosticBuilder &Diag);
+
+static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result,
+                      DiagnosticBuilder &Diag);
+
+static void renameFunc(StringRef NewFuncName,
+                       const MatchFinder::MatchResult &Result,
+                       DiagnosticBuilder &Diag);
+
+static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe,
+                         const MatchFinder::MatchResult &Result,
+                         DiagnosticBuilder &Diag);
+
+static void insertDestCapacityArg(bool IsOverflows, StringRef Name,
+                                  const MatchFinder::MatchResult &Result,
+                                  DiagnosticBuilder &Diag);
+
+static void insertNullTerminatorExpr(StringRef Name,
+                                     const MatchFinder::MatchResult &Result,
+                                     DiagnosticBuilder &Diag);
+
+NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {}
+
+void NotNullTerminatedResultCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions);
+}
+
+void NotNullTerminatedResultCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  PP = &Compiler.getPreprocessor();
+}
+
+namespace {
+AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>,
+              InnerMatcher) {
+  const Expr *SimpleNode = &Node;
+  SimpleNode = SimpleNode->IgnoreParenImpCasts();
+
+  if (InnerMatcher.matches(*SimpleNode, Finder, Builder))
+    return true;
+
+  auto DREHasInit = ignoringImpCasts(
+      declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher))))));
+
+  if (DREHasInit.matches(*SimpleNode, Finder, Builder))
+    return true;
+
+  // - Example:  int getLength(const char *str) { return strlen(str); }
+  auto CallExprReturnInit = ignoringImpCasts(
+      callExpr(callee(functionDecl(hasBody(has(returnStmt(hasReturnValue(
+          ignoringImpCasts(anyOf(DREHasInit, InnerMatcher))))))))));
+
+  if (CallExprReturnInit.matches(*SimpleNode, Finder, Builder))
+    return true;
+
+  // - Example:  int length = getLength(src);
+  auto DREHasReturnInit = ignoringImpCasts(
+      declRefExpr(to(varDecl(hasInitializer(CallExprReturnInit)))));
+
+  if (DREHasReturnInit.matches(*SimpleNode, Finder, Builder))
+    return true;
+
+  const char *const VarDeclName = "variable-declaration";
+  auto DREHasDefinition = ignoringImpCasts(declRefExpr(
+      allOf(to(varDecl().bind(VarDeclName)),
+            hasAncestor(compoundStmt(hasDescendant(binaryOperator(
+                hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))),
+                hasRHS(ignoringImpCasts(InnerMatcher)))))))));
+
+  if (DREHasDefinition.matches(*SimpleNode, Finder, Builder))
+    return true;
+
+  return false;
+}
+} // namespace
+
+void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) {
+  auto IncOp =
+      binaryOperator(hasOperatorName("+"),
+                     hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
+
+  auto DecOp =
+      binaryOperator(hasOperatorName("-"),
+                     hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
+
+  auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp));
+  auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp));
+
+  auto StringTy = type(hasUnqualifiedDesugaredType(recordType(
+      hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));
+
+  auto AnyOfStringTy =
+      anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy))));
+
+  auto CharTy =
+      anyOf(asString("char"), asString("wchar_t"),
+            allOf(anyOf(asString("unsigned char"), asString("signed char")),
+                  type().bind(NotJustCharTyName)));
+
+  auto CharTyArray = hasType(qualType(hasCanonicalType(
+      arrayType(hasElementType(CharTy)).bind(DestArrayTyName))));
+
+  auto CharTyPointer =
+      hasType(qualType(hasCanonicalType(pointerType(pointee(CharTy)))));
+
+  auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer);
+
+  //===--------------------------------------------------------------------===//
+  // The following six cases match problematic length expressions.
+  //===--------------------------------------------------------------------===//
+
+  // - Example:  char src[] = "foo";       strlen(src);
+  auto Strlen =
+      callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen"))))
+          .bind(WrongLengthExprName);
+
+  // - Example:  std::string str = "foo";  str.size();
+  auto SizeOrLength =
+      cxxMemberCallExpr(
+          allOf(on(expr(AnyOfStringTy)),
+                has(memberExpr(member(hasAnyName("size", "length"))))))
+          .bind(WrongLengthExprName);
+
+  // - Example:  char src[] = "foo";       sizeof(src);
+  auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(hasType(qualType(
+      hasCanonicalType(anyOf(arrayType(hasElementType(isAnyCharacter())),
+                             pointerType(pointee(isAnyCharacter())))))))));
+
+  auto WrongLength =
+      anyOf(ignoringImpCasts(Strlen), ignoringImpCasts(SizeOrLength),
+            hasDescendant(Strlen), hasDescendant(SizeOrLength));
+
+  // - Example:  length = strlen(src);
+  auto DREWithoutInc =
+      ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength)))));
+
+  auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength);
+
+  // - Example:  int getLength(const char *str) { return strlen(str); }
+  auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl(
+      hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc))))))));
+
+  // - Example:  int length = getLength(src);
+  auto DREHasReturnWithoutInc = ignoringImpCasts(
+      declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc)))));
+
+  auto AnyOfWrongLengthInit =
+      anyOf(AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc,
+            DREHasReturnWithoutInc);
+
+  enum class StrlenKind { WithInc, WithoutInc };
+
+  const auto AnyOfLengthExpr = [=](StrlenKind LengthKind) {
+    return ignoringImpCasts(allOf(
+        unless(hasDefinition(SizeOfCharExpr)),
+        anyOf(allOf((LengthKind == StrlenKind::WithoutInc)
+                        ? ignoringImpCasts(unless(hasDefinition(HasIncOp)))
+                        : ignoringImpCasts(
+                              allOf(hasDefinition(HasIncOp),
+                                    unless(hasDefinition(HasDecOp)))),
+                    AnyOfWrongLengthInit),
+              ignoringImpCasts(integerLiteral().bind(WrongLengthExprName))),
+        expr().bind(LengthExprName)));
+  };
+
+  auto LengthWithoutInc = AnyOfLengthExpr(StrlenKind::WithoutInc);
+  auto LengthWithInc = AnyOfLengthExpr(StrlenKind::WithInc);
+
+  //===--------------------------------------------------------------------===//
+  // The following five cases match the 'destination' array length's
+  // expression which is used in memcpy() and memmove() matchers.
+  //===--------------------------------------------------------------------===//
+
+  auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1)));
+
+  auto MallocLengthExpr = allOf(
+      anyOf(argumentCountIs(1), argumentCountIs(2)),
+      hasAnyArgument(allOf(unless(SizeExpr),
+                           expr(ignoringImpCasts(anyOf(HasIncOp, anything())))
+                               .bind(DestMallocExprName))));
+
+  // - Example:  (char *)malloc(length);
+  auto DestMalloc = anyOf(castExpr(has(callExpr(MallocLengthExpr))),
+                          callExpr(MallocLengthExpr));
+
+  // - Example:  new char[length];
+  auto DestCXXNewExpr = ignoringImpCasts(
+      cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName))));
+
+  auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr);
+
+  // - Example:  char dest[13];  or  char dest[length];
+  auto DestArrayTyDecl = declRefExpr(
+      to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName),
+               varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName))));
+
+  // - Example:  foo[bar[baz]].qux; (or just ParmVarDecl)
+  auto DestUnknownDecl =
+      declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)),
+                        expr().bind(UnknownDestName)));
+
+  auto AnyOfDestDecl =
+      allOf(anyOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl)),
+                  DestUnknownDecl, anything()),
+            expr().bind(DestExprName));
+
+  auto SrcDecl = declRefExpr(
+      allOf(to(decl().bind(SrcVarDeclName)),
+            anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)),
+                  expr().bind(SrcExprName))));
+
+  auto SrcDeclMayInBinOp =
+      anyOf(ignoringImpCasts(SrcDecl), hasDescendant(SrcDecl));
+
+  auto AnyOfSrcDecl = anyOf(ignoringImpCasts(stringLiteral().bind(SrcExprName)),
+                            SrcDeclMayInBinOp);
+
+  auto NullTerminatorExpr = binaryOperator(
+      hasLHS(hasDescendant(
+          declRefExpr(to(varDecl(equalsBoundNode(DestVarDeclName)))))),
+      hasRHS(ignoringImpCasts(
+          anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0))))));
+
+  //===--------------------------------------------------------------------===//
+  // The following nineteen cases match problematic function calls.
+  //===--------------------------------------------------------------------===//
+
+  const auto WithoutSrc = [=](StringRef Name, int LengthPos,
+                              StrlenKind LengthKind) {
+    return allOf(
+        callee(functionDecl(hasName(Name))),
+        hasArgument(
+            0, allOf(AnyOfDestDecl, unless(hasAncestor(compoundStmt(
+                                        hasDescendant(NullTerminatorExpr)))))),
+        hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc)
+                                   ? LengthWithoutInc
+                                   : LengthWithInc));
+  };
+
+  const auto WithSrc = [=](StringRef Name, int SourcePos, int LengthPos,
+                           StrlenKind LengthKind) {
+    return allOf(callee(functionDecl(hasName(Name))),
+                 hasArgument(SourcePos ? 0 : 1,
+                             allOf(AnyOfDestDecl,
+                                   unless(hasAncestor(compoundStmt(
+                                       hasDescendant(NullTerminatorExpr)))))),
+                 hasArgument(SourcePos, AnyOfSrcDecl),
+                 hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc)
+                                            ? LengthWithoutInc
+                                            : LengthWithInc));
+  };
+
+  auto Memcpy = WithSrc("::memcpy", 1, 2, StrlenKind::WithoutInc);
+  auto Wmemcpy = WithSrc("::wmemcpy", 1, 2, StrlenKind::WithoutInc);
+  auto Memcpy_s = WithSrc("::memcpy_s", 2, 3, StrlenKind::WithoutInc);
+  auto Wmemcpy_s = WithSrc("::wmemcpy_s", 2, 3, StrlenKind::WithoutInc);
+  auto Memchr = WithSrc("::memchr", 0, 2, StrlenKind::WithoutInc);
+  auto Wmemchr = WithSrc("::wmemchr", 0, 2, StrlenKind::WithoutInc);
+  auto Memmove = WithSrc("::memmove", 1, 2, StrlenKind::WithoutInc);
+  auto Wmemmove = WithSrc("::wmemmove", 1, 2, StrlenKind::WithoutInc);
+  auto Memmove_s = WithSrc("::memmove_s", 2, 3, StrlenKind::WithoutInc);
+  auto Wmemmove_s = WithSrc("::wmemmove_s", 2, 3, StrlenKind::WithoutInc);
+  auto Memset = WithoutSrc("::memset", 2, StrlenKind::WithInc);
+  auto Wmemset = WithoutSrc("::wmemset", 2, StrlenKind::WithInc);
+  auto Strerror_s = WithoutSrc("::strerror_s", 1, StrlenKind::WithoutInc);
+  auto StrncmpLHS = WithSrc("::strncmp", 1, 2, StrlenKind::WithInc);
+  auto WcsncmpLHS = WithSrc("::wcsncmp", 1, 2, StrlenKind::WithInc);
+  auto StrncmpRHS = WithSrc("::strncmp", 0, 2, StrlenKind::WithInc);
+  auto WcsncmpRHS = WithSrc("::wcsncmp", 0, 2, StrlenKind::WithInc);
+  auto Strxfrm = WithSrc("::strxfrm", 1, 2, StrlenKind::WithoutInc);
+  auto Wcsxfrm = WithSrc("::wcsxfrm", 1, 2, StrlenKind::WithoutInc);
+
+  auto AnyOfMatchers =
+      anyOf(Memcpy, Wmemcpy, Memcpy_s, Wmemcpy_s, Memchr, Wmemchr, Memmove,
+            Wmemmove, Memmove_s, Wmemmove_s, Memset, Wmemset, Strerror_s,
+            StrncmpLHS, WcsncmpLHS, StrncmpRHS, WcsncmpRHS, Strxfrm, Wcsxfrm);
+
+  Finder->addMatcher(callExpr(AnyOfMatchers).bind(FuncExprName), this);
+
+  Finder->addMatcher(
+      castExpr(has(callExpr(anyOf(Memchr, Wmemchr)).bind(FuncExprName)))
+          .bind(CastExprName),
+      this);
+}
+
+void NotNullTerminatedResultCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  if (FuncExpr->getBeginLoc().isMacroID())
+    return;
+
+  if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) {
+    Optional<bool> AreSafeFunctionsWanted;
+
+    Preprocessor::macro_iterator It = PP->macro_begin();
+    while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) {
+      if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") {
+        const auto *MI = PP->getMacroInfo(It->first);
+        const auto &T = MI->tokens().back();
+        StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
+        llvm::APInt IntValue;
+        ValueStr.getAsInteger(10, IntValue);
+        AreSafeFunctionsWanted = IntValue.getZExtValue();
+      }
+
+      ++It;
+    }
+
+    if (AreSafeFunctionsWanted.hasValue())
+      UseSafeFunctions = AreSafeFunctionsWanted.getValue();
+  }
+
+  StringRef Name = FuncExpr->getDirectCallee()->getName();
+  if (Name.startswith("mem") || Name.startswith("wmem"))
+    memoryHandlerFunctionFix(Name, Result);
+  else if (Name == "strerror_s")
+    strerror_sFix(Result);
+  else if (Name.endswith("ncmp"))
+    ncmpFix(Name, Result);
+  else if (Name.endswith("xfrm"))
+    xfrmFix(Name, Result);
+}
+
+void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(
+    StringRef Name, const MatchFinder::MatchResult &Result) {
+  if (isCorrectGivenLength(Result))
+    return;
+
+  if (Name.endswith("chr")) {
+    memchrFix(Name, Result);
+    return;
+  }
+
+  if ((Name.contains("cpy") || Name.contains("move")) &&
+      isDestAndSrcEquals(Result))
+    return;
+
+  auto Diag =
+      diag(Result.Nodes.getNodeAs<CallExpr>(FuncExprName)->getBeginLoc(),
+           "the result from calling '%0' is not null-terminated")
+      << Name;
+
+  if (Name.endswith("cpy"))
+    memcpyFix(Name, Result, Diag);
+  else if (Name.endswith("cpy_s"))
+    memcpy_sFix(Name, Result, Diag);
+  else if (Name.endswith("move"))
+    memmoveFix(Name, Result, Diag);
+  else if (Name.endswith("move_s")) {
+    destCapacityFix(Result, Diag);
+    lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag);
+  } else if (Name.endswith("set")) {
+    lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag);
+  }
+}
+
+void NotNullTerminatedResultCheck::memcpyFix(
+    StringRef Name, const MatchFinder::MatchResult &Result,
+    DiagnosticBuilder &Diag) {
+  bool IsOverflows = destCapacityFix(Result, Diag);
+
+  // If it cannot be rewritten to string handler function.
+  if (Result.Nodes.getNodeAs<Type>(NotJustCharTyName)) {
+    if (UseSafeFunctions && isKnownDest(Result)) {
+      renameFunc((Name[0] != 'w') ? "memcpy_s" : "wmemcpy_s", Result, Diag);
+      insertDestCapacityArg(IsOverflows, Name, Result, Diag);
+    }
+
+    lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag);
+    return;
+  }
+
+  bool IsCopy =
+      isLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result);
+
+  bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) &&
+                !isDestBasedOnGivenLength(Result);
+
+  bool IsDestLengthNotRequired =
+      IsSafe && getLangOpts().CPlusPlus &&
+      Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName);
+
+  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);
+
+  if (IsSafe && !IsDestLengthNotRequired)
+    insertDestCapacityArg(IsOverflows, Name, Result, Diag);
+
+  if (IsCopy)
+    removeArg(2, Result, Diag);
+
+  if (!IsCopy && !IsSafe)
+    insertNullTerminatorExpr(Name, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::memcpy_sFix(
+    StringRef Name, const MatchFinder::MatchResult &Result,
+    DiagnosticBuilder &Diag) {
+  bool IsOverflows = destCapacityFix(Result, Diag);
+
+  if (Result.Nodes.getNodeAs<Type>(NotJustCharTyName)) {
+    lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag);
+    return;
+  }
+
+  bool RemoveDestLength = getLangOpts().CPlusPlus &&
+                          Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName);
+  bool IsCopy = isLengthEqualToSrcLength(Result);
+  bool IsSafe = IsOverflows;
+
+  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);
+
+  if (!IsSafe || (IsSafe && RemoveDestLength))
+    removeArg(1, Result, Diag);
+  else if (IsOverflows && isKnownDest(Result))
+    lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag);
+
+  if (IsCopy)
+    removeArg(3, Result, Diag);
+
+  if (!IsCopy && !IsSafe)
+    insertNullTerminatorExpr(Name, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::memchrFix(
+    StringRef Name, const MatchFinder::MatchResult &Result) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  if (const auto GivenCL =
+          dyn_cast_or_null<CharacterLiteral>(FuncExpr->getArg(1)))
+    if (GivenCL->getValue() != 0)
+      return;
+
+  auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
+                   "the length is too short to include the null terminator");
+
+  if (const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(CastExprName)) {
+    const auto CastRemoveFix = FixItHint::CreateRemoval(SourceRange(
+        CastExpr->getBeginLoc(), FuncExpr->getBeginLoc().getLocWithOffset(-1)));
+    Diag << CastRemoveFix;
+  }
+  StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr";
+  renameFunc(NewFuncName, Result, Diag);
+  removeArg(2, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::memmoveFix(
+    StringRef Name, const MatchFinder::MatchResult &Result,
+    DiagnosticBuilder &Diag) {
+  bool IsOverflows = destCapacityFix(Result, Diag);
+
+  if (UseSafeFunctions && isKnownDest(Result)) {
+    renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag);
+    insertDestCapacityArg(IsOverflows, Name, Result, Diag);
+  }
+
+  lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::strerror_sFix(
+    const MatchFinder::MatchResult &Result) {
+  StringRef Name = "strerror_s";
+  auto Diag =
+      diag(Result.Nodes.getNodeAs<CallExpr>(FuncExprName)->getBeginLoc(),
+           "the result from calling '%0' is not null-terminated and "
+           "missing the last character of the error message")
+      << Name;
+
+  destCapacityFix(Result, Diag);
+  lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::ncmpFix(
+    StringRef Name, const MatchFinder::MatchResult &Result) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  const Expr *FirstArgExpr = FuncExpr->getArg(0)->IgnoreImpCasts();
+  const Expr *SecondArgExpr = FuncExpr->getArg(1)->IgnoreImpCasts();
+  bool IsLengthTooLong = false;
+
+  if (const auto *LengthExpr =
+          Result.Nodes.getNodeAs<CallExpr>(WrongLengthExprName)) {
+    const Expr *LengthExprArg = LengthExpr->getArg(0);
+    StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim(' ');
+    StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim(' ');
+    StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim(' ');
+    IsLengthTooLong =
+        LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr;
+  } else {
+    int SrcLength =
+        getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
+    int GivenLength = getGivenLength(Result);
+    IsLengthTooLong = GivenLength - 1 == SrcLength;
+  }
+
+  if (!IsLengthTooLong && !isStringDataAndLength(Result))
+    return;
+
+  auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
+                   "comparison length is too long and might lead to a "
+                   "buffer overflow");
+
+  lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag);
+}
+
+void NotNullTerminatedResultCheck::xfrmFix(
+    StringRef Name, const MatchFinder::MatchResult &Result) {
+  if (!isDestCapacityOverflows(Result))
+    return;
+
+  auto Diag =
+      diag(Result.Nodes.getNodeAs<CallExpr>(FuncExprName)->getBeginLoc(),
+           "the result from calling '%0' is not null-terminated")
+      << Name;
+
+  destCapacityFix(Result, Diag);
+  lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag);
+}
+
+//===---------------------------------------------------------------------===//
+// All the helper functions.
+//===---------------------------------------------------------------------===//
+
+static StringRef exprToStr(const Expr *E,
+                           const MatchFinder::MatchResult &Result) {
+  if (!E)
+    return "";
+
+  return Lexer::getSourceText(
+      CharSourceRange::getTokenRange(E->getSourceRange()),
+      *Result.SourceManager, Result.Context->getLangOpts(), 0);
+}
+
+static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) {
+  if (const auto *DestVD = Result.Nodes.getNodeAs<Decl>(DestVarDeclName))
+    if (const auto *SrcVD = Result.Nodes.getNodeAs<Decl>(SrcVarDeclName))
+      return DestVD->getCanonicalDecl() == SrcVD->getCanonicalDecl();
+
+  return false;
+}
+
+static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) {
+  if (Result.Nodes.getNodeAs<IntegerLiteral>(WrongLengthExprName))
+    return !isLengthEqualToSrcLength(Result);
+
+  return false;
+}
+
+static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) {
+  if (const auto *DestMalloc = Result.Nodes.getNodeAs<Expr>(DestMallocExprName))
+    return DestMalloc;
+
+  if (const auto *DestTy = Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName))
+    if (const auto *DestVAT = dyn_cast_or_null<VariableArrayType>(DestTy))
+      return DestVAT->getSizeExpr();
+
+  if (const auto *DestVD = Result.Nodes.getNodeAs<VarDecl>(DestVarDeclName))
+    if (const auto DestTL = DestVD->getTypeSourceInfo()->getTypeLoc())
+      if (const auto DestCTL = DestTL.getAs<ConstantArrayTypeLoc>())
+        return DestCTL.getSizeExpr();
+
+  return nullptr;
+}
+
+static int getLength(const Expr *E, const MatchFinder::MatchResult &Result) {
+  llvm::APSInt Length;
+
+  if (const auto *LengthDRE = dyn_cast_or_null<DeclRefExpr>(E))
+    if (const auto *LengthVD = dyn_cast_or_null<VarDecl>(LengthDRE->getDecl()))
+      if (!isa<ParmVarDecl>(LengthVD))
+        if (const Expr *LengthInit = LengthVD->getInit())
+          if (LengthInit->EvaluateAsInt(Length, *Result.Context))
+            return Length.getZExtValue();
+
+  if (const auto *LengthIL = dyn_cast_or_null<IntegerLiteral>(E))
+    return LengthIL->getValue().getZExtValue();
+
+  if (const auto *StrDRE = dyn_cast_or_null<DeclRefExpr>(E))
+    if (const auto *StrVD = dyn_cast_or_null<VarDecl>(StrDRE->getDecl()))
+      if (const Expr *StrInit = StrVD->getInit())
+        if (const auto *StrSL =
+                dyn_cast_or_null<StringLiteral>(StrInit->IgnoreImpCasts()))
+          return StrSL->getLength();
+
+  if (const auto *SrcSL = dyn_cast_or_null<StringLiteral>(E))
+    return SrcSL->getLength();
+
+  return 0;
+}
+
+static int getDestCapacity(const MatchFinder::MatchResult &Result) {
+  if (const auto *DestCapacityExpr = getDestCapacityExpr(Result))
+    return getLength(DestCapacityExpr, Result);
+
+  return 0;
+}
+
+static int getGivenLength(const MatchFinder::MatchResult &Result) {
+  const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName);
+  if (int Length = getLength(LengthExpr, Result))
+    return Length;
+
+  if (const auto *StrlenExpr = dyn_cast_or_null<CallExpr>(LengthExpr))
+    if (StrlenExpr->getNumArgs() > 0)
+      if (const Expr *StrlenArg = StrlenExpr->getArg(0)->IgnoreImpCasts())
+        if (int StrlenArgLength = getLength(StrlenArg, Result))
+          return StrlenArgLength;
+
+  return 0;
+}
+
+static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) {
+  StringRef DestStr =
+      exprToStr(Result.Nodes.getNodeAs<Expr>(DestExprName), Result);
+  StringRef SrcStr =
+      exprToStr(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
+  StringRef GivenLengthStr =
+      exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result);
+
+  bool ProblematicLength =
+      GivenLengthStr.contains(".size") || GivenLengthStr.contains(".length");
+
+  return ProblematicLength &&
+         (SrcStr.contains(".data") || DestStr.contains(".data"));
+}
+
+static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) {
+  if (isStringDataAndLength(Result))
+    return true;
+
+  int GivenLength = getGivenLength(Result);
+
+  // It is the length without the null terminator.
+  int SrcLength = getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
+
+  if (GivenLength != 0 && GivenLength == SrcLength)
+    return true;
+
+  // If 'strlen()' check the VarDecl of the argument is equal to source VarDecl.
+  if (const auto *StrlenExpr = Result.Nodes.getNodeAs<CallExpr>(LengthExprName))
+    if (StrlenExpr->getNumArgs() > 0)
+      if (const auto *StrlenDRE = dyn_cast_or_null<DeclRefExpr>(
+              StrlenExpr->getArg(0)->IgnoreImpCasts()))
+        if (const auto *SrcVD = Result.Nodes.getNodeAs<VarDecl>(SrcVarDeclName))
+          return dyn_cast_or_null<VarDecl>(StrlenDRE->getDecl()) == SrcVD;
+
+  return false;
+}
+
+static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) {
+  if (!isKnownDest(Result))
+    return true;
+
+  const auto *DestCapacityExpr = getDestCapacityExpr(Result);
+  const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName);
+  int DestCapacity = getLength(DestCapacityExpr, Result);
+  int GivenLength = getGivenLength(Result);
+
+  if (GivenLength != 0 && DestCapacity != 0)
+    return isLengthEqualToSrcLength(Result) && DestCapacity == GivenLength;
+
+  StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result);
+  StringRef LengthExprStr = exprToStr(LengthExpr, Result);
+
+  // Assume that it cannot overflow if the expression of the destination
+  // capacity contains '+ 1'.
+  if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1"))
+    return false;
+
+  if (DestCapacityExprStr != "" && DestCapacityExprStr == LengthExprStr)
+    return true;
+
+  return true;
+}
+
+static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) {
+  StringRef DestCapacityExprStr =
+      exprToStr(getDestCapacityExpr(Result), Result).trim(' ');
+  StringRef LengthExprStr =
+      exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result).trim(' ');
+
+  return DestCapacityExprStr != "" && LengthExprStr != "" &&
+         DestCapacityExprStr.contains(LengthExprStr);
+}
+
+static void lengthDecrease(const Expr *LengthExpr,
+                           const MatchFinder::MatchResult &Result,
+                           DiagnosticBuilder &Diag) {
+  // This is the following structure: ((strlen(src) * 2) + 1)
+  //                     InnerOpExpr:   ~~~~~~~~~~~~^~~
+  //                     OuterOpExpr:  ~~~~~~~~~~~~~~~~~~^~~
+  if (const auto *OuterOpExpr =
+          dyn_cast_or_null<BinaryOperator>(LengthExpr->IgnoreParenCasts())) {
+    const Expr *LHSExpr = OuterOpExpr->getLHS();
+    const Expr *RHSExpr = OuterOpExpr->getRHS();
+    const auto *InnerOpExpr =
+        isa<IntegerLiteral>(RHSExpr->IgnoreCasts()) ? LHSExpr : RHSExpr;
+
+    // This is the following structure: ((strlen(src) * 2) + 1)
+    //                  LHSRemoveRange: ~~
+    //                  RHSRemoveRange:                  ~~~~~~
+    SourceRange LHSRemoveRange(LengthExpr->getBeginLoc(),
+                               InnerOpExpr->getBeginLoc().getLocWithOffset(-1));
+    SourceRange RHSRemoveRange(exprLocEnd(InnerOpExpr, Result),
+                               LengthExpr->getEndLoc());
+    const auto LHSRemoveFix = FixItHint::CreateRemoval(LHSRemoveRange);
+    const auto RHSRemoveFix = FixItHint::CreateRemoval(RHSRemoveRange);
+
+    if (LengthExpr->getBeginLoc() == InnerOpExpr->getBeginLoc())
+      Diag << RHSRemoveFix;
+    else if (LengthExpr->getEndLoc() == InnerOpExpr->getEndLoc())
+      Diag << LHSRemoveFix;
+    else
+      Diag << LHSRemoveFix << RHSRemoveFix;
+  } else {
+    const auto InsertDecreaseFix =
+        FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1");
+    Diag << InsertDecreaseFix;
+  }
+}
+
+static void lengthIncrease(const Expr *LengthExpr,
+                           const MatchFinder::MatchResult &Result,
+                           DiagnosticBuilder &Diag) {
+  bool NeedInnerParen = dyn_cast_or_null<BinaryOperator>(LengthExpr) &&
+                        cast<BinaryOperator>(LengthExpr)->getOpcode() != BO_Add;
+
+  if (NeedInnerParen) {
+    const auto InsertFirstParenFix =
+        FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "(");
+    const auto InsertPlusOneAndSecondParenFix =
+        FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result),
+                                   !isInjectUL(Result) ? ") + 1" : ") + 1UL");
+    Diag << InsertFirstParenFix << InsertPlusOneAndSecondParenFix;
+  } else {
+    const auto InsertPlusOneFix =
+        FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result),
+                                   !isInjectUL(Result) ? " + 1" : " + 1UL");
+    Diag << InsertPlusOneFix;
+  }
+}
+
+static void lengthExprHandle(LengthHandleKind LengthHandle,
+                             const Expr *LengthExpr,
+                             const MatchFinder::MatchResult &Result,
+                             DiagnosticBuilder &Diag) {
+  if (!LengthExpr)
+    return;
+
+  bool IsMacroDefinition = false;
+  StringRef LengthExprStr = exprToStr(LengthExpr, Result);
+
+  Preprocessor::macro_iterator It = PP->macro_begin();
+  while (It != PP->macro_end() && !IsMacroDefinition) {
+    if (It->first->getName() == LengthExprStr)
+      IsMacroDefinition = true;
+
+    ++It;
+  }
+
+  if (!IsMacroDefinition) {
+    if (const auto *LengthIL = dyn_cast_or_null<IntegerLiteral>(LengthExpr)) {
+      const size_t NewLength = LengthIL->getValue().getZExtValue() +
+                               (LengthHandle == LengthHandleKind::Increase
+                                    ? (isInjectUL(Result) ? 1UL : 1)
+                                    : -1);
+      const auto NewLengthFix = FixItHint::CreateReplacement(
+          LengthIL->getSourceRange(),
+          (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str());
+      Diag << NewLengthFix;
+      return;
+    }
+
+    if (LengthHandle == LengthHandleKind::Increase)
+      lengthIncrease(LengthExpr, Result, Diag);
+    else
+      lengthDecrease(LengthExpr, Result, Diag);
+  } else {
+    if (LengthHandle == LengthHandleKind::Increase) {
+      const auto InsertPlusOneFix =
+          FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result),
+                                     !isInjectUL(Result) ? " + 1" : " + 1UL");
+      Diag << InsertPlusOneFix;
+    } else {
+      const auto InsertMinusOneFix =
+          FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1");
+      Diag << InsertMinusOneFix;
+    }
+  }
+}
+
+static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos,
+                            const MatchFinder::MatchResult &Result,
+                            DiagnosticBuilder &Diag) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  const Expr *LengthExpr = FuncExpr->getArg(ArgPos)->IgnoreImpCasts();
+  lengthExprHandle(LengthHandle, LengthExpr, Result, Diag);
+}
+
+static bool destCapacityFix(const MatchFinder::MatchResult &Result,
+                            DiagnosticBuilder &Diag) {
+  bool IsOverflows = isDestCapacityOverflows(Result);
+  if (IsOverflows)
+    lengthExprHandle(LengthHandleKind::Increase, getDestCapacityExpr(Result),
+                     Result, Diag);
+
+  return IsOverflows;
+}
+
+static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result,
+                      DiagnosticBuilder &Diag) {
+  // This is the following structure: (src, '\0', strlen(src))
+  //                     ArgToRemove:             ~~~~~~~~~~~
+  //                          LHSArg:       ~~~~
+  //                    RemoveArgFix:           ~~~~~~~~~~~~~
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  const Expr *ArgToRemove = FuncExpr->getArg(ArgPos);
+  const Expr *LHSArg = FuncExpr->getArg(ArgPos - 1);
+  const auto RemoveArgFix = FixItHint::CreateRemoval(
+      SourceRange(exprLocEnd(LHSArg, Result),
+                  exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1)));
+  Diag << RemoveArgFix;
+}
+
+static void renameFunc(StringRef NewFuncName,
+                       const MatchFinder::MatchResult &Result,
+                       DiagnosticBuilder &Diag) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  int FuncNameLength =
+      FuncExpr->getDirectCallee()->getIdentifier()->getLength();
+  SourceRange FuncNameRange(
+      FuncExpr->getBeginLoc(),
+      FuncExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1));
+
+  const auto FuncNameFix =
+      FixItHint::CreateReplacement(FuncNameRange, NewFuncName);
+  Diag << FuncNameFix;
+}
+
+static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe,
+                         const MatchFinder::MatchResult &Result,
+                         DiagnosticBuilder &Diag) {
+  SmallString<10> NewFuncName;
+  NewFuncName = (Name[0] != 'w') ? "str" : "wcs";
+  NewFuncName += IsCopy ? "cpy" : "ncpy";
+  NewFuncName += IsSafe ? "_s" : "";
+  renameFunc(NewFuncName, Result, Diag);
+}
+
+static void insertDestCapacityArg(bool IsOverflows, StringRef Name,
+                                  const MatchFinder::MatchResult &Result,
+                                  DiagnosticBuilder &Diag) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  SmallString<64> NewSecondArg;
+
+  if (int DestLength = getDestCapacity(Result)) {
+    NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str();
+  } else {
+    NewSecondArg =
+        (Twine(exprToStr(getDestCapacityExpr(Result), Result)) +
+         (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : ""))
+            .str();
+  }
+
+  NewSecondArg += ", ";
+  const auto InsertNewArgFix = FixItHint::CreateInsertion(
+      FuncExpr->getArg(1)->getBeginLoc(), NewSecondArg);
+  Diag << InsertNewArgFix;
+}
+
+static void insertNullTerminatorExpr(StringRef Name,
+                                     const MatchFinder::MatchResult &Result,
+                                     DiagnosticBuilder &Diag) {
+  const auto *FuncExpr = Result.Nodes.getNodeAs<CallExpr>(FuncExprName);
+  int FuncLocStartColumn =
+      Result.SourceManager->getPresumedColumnNumber(FuncExpr->getBeginLoc());
+  SourceRange SpaceRange(
+      FuncExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1),
+      FuncExpr->getBeginLoc());
+  StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
+      CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager,
+      Result.Context->getLangOpts(), 0);
+
+  SmallString<128> NewAddNullTermExprStr;
+  NewAddNullTermExprStr =
+      (Twine('\n') + SpaceBeforeStmtStr +
+       exprToStr(Result.Nodes.getNodeAs<Expr>(DestExprName), Result) + "[" +
+       exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result) +
+       "] = " + ((Name[0] != 'w') ? "\'\\0\';" : "L\'\\0\';"))
+          .str();
+
+  const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion(
+      exprLocEnd(FuncExpr, Result).getLocWithOffset(1), NewAddNullTermExprStr);
+  Diag << AddNullTerminatorExprFix;
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.h?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/NotNullTerminatedResultCheck.h Fri Oct 12 10:22:36 2018
@@ -0,0 +1,67 @@
+//===--- NotNullTerminatedResultCheck.h - clang-tidy ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds function calls where it is possible to cause a not null-terminated
+/// result. Usually the proper length of a string is ``strlen(src) + 1`` or
+/// equal length of this expression, because the null terminator needs an extra
+/// space. Without the null terminator it can result in undefined behaviour
+/// when the string is read.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-not-null-terminated-result.html
+class NotNullTerminatedResultCheck : public ClangTidyCheck {
+public:
+  NotNullTerminatedResultCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+private:
+  // If non-zero it is specifying if the target environment is considered to
+  // implement '_s' suffixed memory and string handler functions which are safer
+  // than older version (e.g. 'memcpy_s()'). The default value is ``1``.
+  const int WantToUseSafeFunctions;
+
+  bool UseSafeFunctions = false;
+
+  void memoryHandlerFunctionFix(
+      StringRef Name, const ast_matchers::MatchFinder::MatchResult &Result);
+  void memcpyFix(StringRef Name,
+                 const ast_matchers::MatchFinder::MatchResult &Result,
+                 DiagnosticBuilder &Diag);
+  void memcpy_sFix(StringRef Name,
+                   const ast_matchers::MatchFinder::MatchResult &Result,
+                   DiagnosticBuilder &Diag);
+  void memchrFix(StringRef Name,
+                 const ast_matchers::MatchFinder::MatchResult &Result);
+  void memmoveFix(StringRef Name,
+                  const ast_matchers::MatchFinder::MatchResult &Result,
+                  DiagnosticBuilder &Diag);
+  void strerror_sFix(const ast_matchers::MatchFinder::MatchResult &Result);
+  void ncmpFix(StringRef Name,
+               const ast_matchers::MatchFinder::MatchResult &Result);
+  void xfrmFix(StringRef Name,
+               const ast_matchers::MatchFinder::MatchResult &Result);
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=344374&r1=344373&r2=344374&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Oct 12 10:22:36 2018
@@ -116,6 +116,15 @@ Improvements to clang-tidy
   Detects usage of the deprecated member types of ``std::ios_base`` and replaces
   those that have a non-deprecated equivalent.
 
+- New :doc:`bugprone-not-null-terminated-result
+  <clang-tidy/checks/bugprone-not-null-terminated-result>` check
+
+  Finds function calls where it is possible to cause a not null-terminated
+  result. Usually the proper length of a string is ``strlen(src) + 1`` or equal
+  length of this expression, because the null terminator needs an extra space.
+  Without the null terminator it can result in undefined behaviour when the
+  string is read.
+
 - New :doc:`readability-magic-numbers
   <clang-tidy/checks/readability-magic-numbers>` check.
 

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst Fri Oct 12 10:22:36 2018
@@ -0,0 +1,132 @@
+.. title:: clang-tidy - bugprone-not-null-terminated-result
+
+bugprone-not-null-terminated-result
+===================================
+
+Finds function calls where it is possible to cause a not null-terminated result.
+Usually the proper length of a string is ``strlen(src) + 1`` or equal length of
+this expression, because the null terminator needs an extra space. Without the 
+null terminator it can result in undefined behaviour when the string is read.
+
+The following function calls are checked:
+
+``memcpy``, ``wmemcpy``, ``memcpy_s``, ``wmemcpy_s``, ``memchr``, ``wmemchr``,
+``memmove``, ``wmemmove``, ``memmove_s``, ``wmemmove_s``, ``memset``,
+``wmemset``, ``strerror_s``, ``strncmp``, ``wcsncmp``, ``strxfrm``, ``wcsxfrm``
+
+The following is a real-world example where the programmer forgot to increase
+the passed third argument, which is ``size_t length``. That is why the length
+of the allocated memory is problematic too.
+
+  .. code-block:: c
+
+    static char *StringCpy(const std::string &str) {
+      char *result = reinterpret_cast<char *>(malloc(str.size()));
+      memcpy(result, str.data(), str.size());
+      return result;
+    }
+
+In addition to issuing warnings, fix-it rewrites all the necessary code. If it
+is necessary, the buffer size will be increased to hold the null terminator.
+
+  .. code-block:: c
+
+    static char *StringCpy(const std::string &str) {
+      char *result = reinterpret_cast<char *>(malloc(str.size() + 1));
+      strcpy(result, str.data());
+      return result;
+    }
+
+.. _MemcpyTransformation:
+
+Transformation rules of 'memcpy()'
+----------------------------------
+
+It is possible to rewrite the ``memcpy()`` and ``memcpy_s()`` calls as the
+following four functions:  ``strcpy()``, ``strncpy()``, ``strcpy_s()``,
+``strncpy_s()``, where the latter two are the safer versions of the former two.
+Respectively it is possible to rewrite ``wmemcpy()`` functions in the same way.
+
+Rewrite to a string handler function is not possible:
+
+- If the type of the destination array is not just ``char`` (``unsigned char``
+  or ``signed char``), that means the new function is cannot be any string
+  handler function. Fix-it adds ``+ 1`` to the given length of copy function.
+
+Rewrite based on the destination array:
+
+- If copy to the destination array cannot *overflow then the new function should
+  be the older copy function (ending with ``cpy``), because it is more
+  efficient than the safe version.
+
+- If copy to the destination array can *overflow and
+  ``AreSafeFunctionsAvailable`` is set to ``Yes``, ``y`` or non-zero and it is
+  possible to obtain the capacity of the destination array then the new function
+  could be the safe version (ending with ``cpy_s``).
+
+- If the new function is could be safe version and C++ files are analysed then
+  the length of the destination array can be omitted.
+
+- *It is possible to overflow:
+  - Unknown the capacity of the destination array.
+  - If the given length is equal to the destination capacity.
+
+Rewrite based on the length of the source string:
+
+- If the given length is ``strlen(source)`` or equal length of this expression
+  then the new function should be the older copy function (ending with ``cpy``),
+  as it is more efficient than the safe version.
+
+- Otherwise we assume that the programmer wanted to copy `n` characters, so the
+  new function is ``ncpy``-like which is could be safe.
+
+Transformations with 'strlen()' or equal length of this expression
+------------------------------------------------------------------
+
+In general, the following transformations are could happen:
+
+(Note: If a wide-character handler function exists of the following functions
+it handled in the same way.)
+
+Memory handler functions
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- ``memcpy``: See in the
+    :ref:`Transformation rules of 'memcpy()'<MemcpyTransformation>` section.
+
+- ``memchr``:
+  - Usually there is a C-style cast, and it is needed to be removed, because the
+    new function ``strchr``'s return type is correct.
+  - Also the given length is not needed in the new function.
+
+- ``memmove``:
+  - If safe functions are available the new function is ``memmove_s``, it has
+    four arguments:
+    - destination array,
+    - length of the destination array,
+    - source string,
+    - length of the source string which is incremented by one.
+  - If safe functions are not available the given length is incremented by one.
+
+- ``memmove_s``: given length is incremented by one.
+
+- ``memset``: given length has to be truncated without the ``+ 1``.
+
+String handler functions
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+- ``strerror_s``: given length is incremented by one.
+
+- ``strncmp``: If the third argument is the first or the second argument's
+    ``length + 1``, then it has to be truncated without the ``+ 1`` operation.
+
+- ``strxfrm``: given length is incremented by one.
+
+Options
+-------
+
+.. option::  WantToUseSafeFunctions
+
+   An integer non-zero value specifying if the target environment is considered
+   to implement '_s' suffixed memory and string handler functions which are
+   safer than older version (e.g. 'memcpy_s()'). The default value is ``1``.

Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=344374&r1=344373&r2=344374&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Oct 12 10:22:36 2018
@@ -44,6 +44,7 @@ Clang-Tidy Checks
    bugprone-misplaced-widening-cast
    bugprone-move-forwarding-reference
    bugprone-multiple-statement-macro
+   bugprone-not-null-terminated-result
    bugprone-parent-virtual-call
    bugprone-sizeof-container
    bugprone-sizeof-expression

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c Fri Oct 12 10:22:36 2018
@@ -0,0 +1,106 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c11
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+char *strerror(int);
+char *strchr(const char *, int);
+errno_t *strncpy_s(char *, const char *, size_t);
+errno_t strerror_s(char *, size_t, int);
+int strncmp(const char *, const char *, size_t);
+size_t strxfrm(char *, const char *, size_t);
+
+void *memchr(const void *, int, size_t);
+void *memset(void *, int, size_t);
+
+int getLengthWithInc(const char *str) {
+  return strlen(str) + 1;
+}
+
+
+void bad_memchr(char *position, const char *src) {
+  int length = strlen(src);
+  position = (char *)memchr(src, '\0', length);
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: position = strchr(src, '\0');
+}
+
+void good_memchr(char *pos, const char *src) {
+  pos = strchr(src, '\0');
+}
+
+void bad_memset_1(const char *src) {
+  char dest[13];
+  memset(dest, '-', getLengthWithInc(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: memset(dest, '-', getLengthWithInc(src) - 1);
+}
+
+void good_memset1(const char *src) {
+  char dst[13];
+  memset(dst, '-', getLengthWithInc(src) - 1);
+}
+
+void bad_strerror_s(int errno) {
+  char dest[13];
+  int length = strlen(strerror(errno));
+  strerror_s(dest, length, errno);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest[14];
+  // CHECK-FIXES-NEXT: int length = strlen(strerror(errno));
+  // CHECK-FIXES-NEXT: strerror_s(dest, length + 1, errno);
+}
+
+void good_strerror_s(int errno) {
+  char dst[14];
+  int length = strlen(strerror(errno));
+  strerror_s(dst, length + 1, errno);
+}
+
+int bad_strncmp_1(char *str1, const char *str2) {
+  int length = strlen(str1) + 1;
+  return strncmp(str1, str2, length);
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str1, str2, length - 1);
+}
+
+int good_strncmp_1(char *str1, const char *str2) {
+  int length = strlen(str1) + 1;
+  return strncmp(str1, str2, length - 1);
+}
+
+int bad_strncmp_2(char *str2) {
+  return strncmp(str2, "foobar", (strlen("foobar") + 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str2, "foobar", strlen("foobar"));
+}
+
+int bad_strncmp_3(char *str3) {
+  return strncmp(str3, "foobar", 1 + strlen("foobar"));
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str3, "foobar", strlen("foobar"));
+}
+
+int good_strncmp_2_3(char *str) {
+  return strncmp(str, "foobar", strlen("foobar"));
+}
+
+void bad_strxfrm(const char *long_source_name) {
+  char long_destination_name[13];
+  int very_long_length_definition_name = strlen(long_source_name);
+  strxfrm(long_destination_name, long_source_name,
+          very_long_length_definition_name);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char long_destination_name[14];
+  // CHECK-FIXES-NEXT: int very_long_length_definition_name = strlen(long_source_name);
+  // CHECK-FIXES-NEXT: strxfrm(long_destination_name, long_source_name,
+  // CHECK-FIXES-NEXT: very_long_length_definition_name + 1);
+}
+
+void good_strxfrm(const char *long_source_name) {
+  char long_destination_name[14];
+  int very_long_length_definition_name = strlen(long_source_name);
+  strxfrm(long_destination_name, long_source_name,
+          very_long_length_definition_name + 1);
+}

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c Fri Oct 12 10:22:36 2018
@@ -0,0 +1,78 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: bugprone-not-null-terminated-result.WantToUseSafeFunctions, \
+// RUN:   value: 1}]}" \
+// RUN: -- -std=c11
+
+// It is not defined therefore the safe functions are unavailable.
+// #define __STDC_LIB_EXT1__ 1
+
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+void *malloc(size_t);
+
+char *strcpy(char *, const char *);
+void *memcpy(void *, const void *, size_t);
+
+
+//===----------------------------------------------------------------------===//
+// memcpy() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dest00[13];
+  memcpy(dest00, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: memcpy(dest00, src, strlen(src) + 1);
+}
+
+void good_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dst00[13];
+  memcpy(dst00, src, strlen(src) + 1);
+}
+
+void bad_memcpy_known_dest(const char *src) {
+  char dest01[13];
+  memcpy(dest01, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy(dest01, src);
+}
+
+void good_memcpy_known_dest(const char *src) {
+  char dst01[13];
+  strcpy(dst01, src);
+}
+
+//===----------------------------------------------------------------------===//
+// memcpy() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_full_source_length(const char *src) {
+  char dest20[13];
+  memcpy(dest20, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy(dest20, src);
+}
+
+void good_memcpy_full_source_length(const char *src) {
+  char dst20[13];
+  strcpy(dst20, src);
+}
+
+void bad_memcpy_partial_source_length(const char *src) {
+  char dest21[13];
+  memcpy(dest21, src, strlen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncpy(dest21, src, strlen(src) - 1);
+  // CHECK-FIXES-NEXT: dest21[strlen(src) - 1] = '\0';
+}
+
+void good_memcpy_partial_source_length(const char *src) {
+  char dst21[13];
+  strncpy(dst21, src, strlen(src) - 1);
+  dst21[strlen(src) - 1] = '\0';
+}
+

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp Fri Oct 12 10:22:36 2018
@@ -0,0 +1,160 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c++11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+namespace std {
+template <typename T>
+struct basic_string {
+  basic_string();
+  const T *data() const;
+  unsigned long size() const;
+};
+typedef basic_string<char> string;
+}
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+void *malloc(size_t);
+void *realloc(void *, size_t);
+
+template <size_t size>
+errno_t strncpy_s(char (&dest)[size], const char *src, size_t length);
+errno_t strncpy_s(char *, size_t, const char *, size_t);
+
+template <size_t size>
+char *strncpy(char (&dest)[size], const char *src, size_t length);
+char *strncpy(char *, const char *, size_t);
+
+template <size_t size>
+errno_t strcpy_s(char (&dest)[size], const char *);
+errno_t strcpy_s(char *, size_t, const char *);
+
+template <size_t size>
+char *strcpy(char (&dest)[size], const char *);
+char *strcpy(char *, const char *);
+
+errno_t memcpy_s(void *, size_t, const void *, size_t);
+void *memcpy(void *, const void *, size_t);
+
+
+//===----------------------------------------------------------------------===//
+// memcpy() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dest00[13];
+  memcpy(dest00, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: unsigned char dest00[14];
+  // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1);
+}
+
+void good_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dst00[14];
+  memcpy_s(dst00, 14, src, strlen(src) + 1);
+}
+
+void bad_memcpy_known_dest(const char *src) {
+  char dest01[13];
+  memcpy(dest01, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: dest01[14];
+  // CHECK-FIXES-NEXT: strcpy_s(dest01, src);
+}
+
+void good_memcpy_known_dest(const char *src) {
+  char dst01[14];
+  strcpy_s(dst01, src);
+}
+
+//===----------------------------------------------------------------------===//
+// memcpy() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_full_source_length(std::string src) {
+  char *dest20;
+  dest20 = reinterpret_cast<char *>(malloc(src.size()));
+  memcpy(dest20, src.data(), src.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: dest20 = reinterpret_cast<char *>(malloc(src.size() + 1));
+  // CHECK-FIXES-NEXT: strcpy(dest20, src.data());
+}
+
+void good_memcpy_full_source_length(std::string src) {
+  char dst20[14];
+  strcpy_s(dst20, src.data());
+}
+
+void bad_memcpy_partial_source_length(const char *src) {
+  char dest21[13];
+  memcpy(dest21, src, strlen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest21[14];
+  // CHECK-FIXES-NEXT: strncpy_s(dest21, src, strlen(src) - 1);
+}
+
+void good_memcpy_partial_source_length(const char *src) {
+  char dst21[14];
+  strncpy_s(dst21, src, strlen(src) - 1);
+}
+
+
+//===----------------------------------------------------------------------===//
+// memcpy_s() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_s_unknown_dest(char *dest40, const char *src) {
+  memcpy_s(dest40, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy_s(dest40, 13, src);
+}
+
+void good_memcpy_s_unknown_dest(char *dst40, const char *src) {
+  strcpy_s(dst40, 13, src);
+}
+
+void bad_memcpy_s_known_dest(const char *src) {
+  char dest41[13];
+  memcpy_s(dest41, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest41[14];
+  // CHECK-FIXES: strcpy_s(dest41, src);
+}
+
+void good_memcpy_s_known_dest(const char *src) {
+  char dst41[14];
+  strcpy_s(dst41, src);
+}
+
+//===----------------------------------------------------------------------===//
+// memcpy_s() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_s_full_source_length(const char *src) {
+  char dest60[13];
+  memcpy_s(dest60, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest60[14];
+  // CHECK-FIXES-NEXT: strcpy_s(dest60, src);
+}
+
+void good_memcpy_s_full_source_length(const char *src) {
+  char dst60[14];
+  strcpy_s(dst60, src);
+}
+
+void bad_memcpy_s_partial_source_length(const char *src) {
+  char dest61[13];
+  memcpy_s(dest61, 13, src, strlen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest61[14];
+  // CHECK-FIXES-NEXT: strncpy_s(dest61, src, strlen(src) - 1);
+}
+
+void good_memcpy_s_partial_source_length(const char *src) {
+  char dst61[14];
+  strncpy_s(dst61, src, strlen(src) - 1);
+}
+

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c Fri Oct 12 10:22:36 2018
@@ -0,0 +1,115 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+void *malloc(size_t);
+void *realloc(void *, size_t);
+
+errno_t strncpy_s(char *, size_t, const char *, size_t);
+errno_t strcpy_s(char *, size_t, const char *);
+char *strcpy(char *, const char *);
+
+errno_t memcpy_s(void *, size_t, const void *, size_t);
+void *memcpy(void *, const void *, size_t);
+
+#define SRC_LENGTH 3
+#define SRC "foo"
+
+
+void good_memcpy_known_src() {
+  char dest[13];
+  char src[] = "foobar";
+  memcpy(dest, src, sizeof(src));
+}
+
+void good_memcpy_null_terminated(const char *src) {
+  char dest[13];
+  const int length = strlen(src);
+  memcpy(dest, src, length);
+  dest[length] = '\0';
+}
+
+void good_memcpy_proper_length(const char *src) {
+  char *dest = 0;
+  int length = strlen(src) + 1;
+  dest = (char *)malloc(length);
+  memcpy(dest, src, length);
+}
+
+void may_bad_memcpy_unknown_length(const char *src, int length) {
+  char dest[13];
+  memcpy(dest, src, length);
+}
+
+void may_bad_memcpy_const_length(const char *src) {
+  char dest[13];
+  memcpy(dest, src, 12);
+}
+
+void bad_memcpy_unknown_dest(char *dest01, const char *src) {
+  memcpy(dest01, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy(dest01, src);
+}
+
+void good_memcpy_unknown_dest(char *dst01, const char *src) {
+  strcpy(dst01, src);
+}
+
+void bad_memcpy_variable_array(int dest_length) {
+  char dest02[dest_length + 1];
+  memcpy(dest02, "foobarbazqux", strlen("foobarbazqux"));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy(dest02, "foobarbazqux");
+}
+
+void good_memcpy_variable_array(int dest_length) {
+  char dst02[dest_length + 1];
+  strcpy(dst02, "foobarbazqux");
+}
+
+void bad_memcpy_equal_src_length_and_length() {
+  char dest03[13];
+  const char *src = "foobarbazqux";
+  memcpy(dest03, src, 12);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy(dest03, src);
+}
+
+void good_memcpy_equal_src_length_and_length() {
+  char dst03[13];
+  const char *src = "foobarbazqux";
+  strcpy(dst03, src);
+}
+
+void bad_memcpy_dest_size_overflows(const char *src) {
+  const int length = strlen(src);
+  char *dest04 = (char *)malloc(length);
+  memcpy(dest04, src, length);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char *dest04 = (char *)malloc(length + 1);
+  // CHECK-FIXES-NEXT: strcpy(dest04, src);
+}
+
+void good_memcpy_dest_size_overflows(const char *src) {
+  const int length = strlen(src);
+  char *dst04 = (char *)malloc(length + 1);
+  strcpy(dst04, src);
+}
+
+void bad_memcpy_macro() {
+  unsigned char dest05[13];
+  memcpy(dest05, SRC, SRC_LENGTH);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: memcpy_s(dest05, 13, SRC, SRC_LENGTH + 1);
+}
+
+void good_memcpy_macro() {
+  unsigned char dst05[13];
+  memcpy_s(dst05, 13, SRC, SRC_LENGTH + 1);
+}

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c Fri Oct 12 10:22:36 2018
@@ -0,0 +1,137 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+void *malloc(size_t);
+void *realloc(void *, size_t);
+
+errno_t strncpy_s(char *, size_t, const char *, size_t);
+errno_t strcpy_s(char *, size_t, const char *);
+char *strcpy(char *, const char *);
+
+errno_t memcpy_s(void *, size_t, const void *, size_t);
+void *memcpy(void *, const void *, size_t);
+
+//===----------------------------------------------------------------------===//
+// memcpy() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dest00[13];
+  memcpy(dest00, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: unsigned char dest00[14];
+  // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1);
+}
+
+void good_memcpy_not_just_char_dest(const char *src) {
+  unsigned char dst00[14];
+  memcpy_s(dst00, 14, src, strlen(src) + 1);
+}
+
+void bad_memcpy_known_dest(const char *src) {
+  char dest01[13];
+  memcpy(dest01, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest01[14];
+  // CHECK-FIXES: strcpy_s(dest01, 14, src);
+}
+
+void good_memcpy_known_dest(const char *src) {
+  char dst01[14];
+  strcpy_s(dst01, 14, src);
+}
+
+//===----------------------------------------------------------------------===//
+// memcpy() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_full_source_length(const char *src) {
+  char dest20[13];
+  memcpy(dest20, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest20[14];
+  // CHECK-FIXES-NEXT: strcpy_s(dest20, 14, src);
+}
+
+void good_memcpy_full_source_length(const char *src) {
+  char dst20[14];
+  strcpy_s(dst20, 14, src);
+}
+
+void bad_memcpy_partial_source_length(const char *src) {
+  char dest21[13];
+  memcpy(dest21, src, strlen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest21[14];
+  // CHECK-FIXES-NEXT: strncpy_s(dest21, 14, src, strlen(src) - 1);
+}
+
+void good__memcpy_partial_source_length(const char *src) {
+  char dst21[14];
+  strncpy_s(dst21, 14, src, strlen(src) - 1);
+}
+
+
+//===----------------------------------------------------------------------===//
+// memcpy_s() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_s_unknown_dest(char *dest40, const char *src) {
+  memcpy_s(dest40, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strcpy_s(dest40, 13, src);
+}
+
+void good_memcpy_s_unknown_dest(char *dst40, const char *src) {
+  strcpy_s(dst40, 13, src);
+}
+
+void bad_memcpy_s_known_dest(const char *src) {
+  char dest41[13];
+  memcpy_s(dest41, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest41[14];
+  // CHECK-FIXES-NEXT: strcpy_s(dest41, 14, src);
+}
+
+void good_memcpy_s_known_dest(const char *src) {
+  char dst41[14];
+  strcpy_s(dst41, 14, src);
+}
+
+//===----------------------------------------------------------------------===//
+// memcpy_s() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_memcpy_s_full_source_length(const char *src) {
+  char dest60[13];
+  memcpy_s(dest60, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest60[14];
+  // CHECK-FIXES-NEXT: strcpy_s(dest60, 14, src);
+}
+
+void good_memcpy_s_full_source_length(const char *src) {
+  char dst60[14];
+  strcpy_s(dst60, 14, src);
+}
+
+void bad_memcpy_s_partial_source_length(const char *src) {
+  char dest61[13];
+  memcpy_s(dest61, 13, src, strlen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest61[14];
+  // CHECK-FIXES-NEXT: strncpy_s(dest61, 14, src, strlen(src) - 1);
+}
+
+void good_memcpy_s_partial_source_length(const char *src) {
+  char dst61[14];
+  strncpy_s(dst61, 14, src, strlen(src) - 1);
+}
+

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c Fri Oct 12 10:22:36 2018
@@ -0,0 +1,146 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t strlen(const char *);
+char *strerror(int);
+
+char *strchr(const char *, int);
+errno_t strncpy_s(char *, size_t, const char *, size_t);
+errno_t strerror_s(char *, size_t, int);
+int strncmp(const char *, const char *, size_t);
+size_t strxfrm(char *, const char *, size_t);
+
+void *memchr(const void *, int, size_t);
+void *memmove(void *, const void *, size_t);
+errno_t memmove_s(void *, size_t, const void *, size_t);
+void *memset(void *, int, size_t);
+
+
+void bad_memchr_1(char *position, const char *src) {
+  position = (char *)memchr(src, '\0', strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: position = strchr(src, '\0');
+}
+
+void good_memchr_1(char *pos, const char *src) {
+  pos = strchr(src, '\0');
+}
+
+void bad_memchr_2(char *position) {
+  position = (char *)memchr("foobar", '\0', 6);
+  // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: position = strchr("foobar", '\0');
+}
+
+void good_memchr_2(char *pos) {
+  pos = strchr("foobar", '\0');
+}
+
+
+void bad_memmove(const char *src) {
+  char dest[13];
+  memmove(dest, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest[14];
+  // CHECK-FIXES-NEXT: memmove_s(dest, 14, src, strlen(src) + 1);
+}
+
+void good_memmove(const char *src) {
+  char dst[14];
+  memmove_s(dst, 13, src, strlen(src) + 1);
+}
+
+void bad_memmove_s(char *dest, const char *src) {
+  memmove_s(dest, 13, src, strlen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: memmove_s(dest, 13, src, strlen(src) + 1);
+}
+
+void good_memmove_s_1(char *dest, const char *src) {
+  memmove_s(dest, 13, src, strlen(src) + 1);
+}
+
+void bad_memset(const char *src) {
+  char dest[13];
+  memset(dest, '-', strlen(src) + 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: memset(dest, '-', strlen(src));
+}
+
+void good_memset(const char *src) {
+  char dst[13];
+  memset(dst, '-', strlen(src));
+}
+
+void bad_strerror_s(int errno) {
+  char dest[13];
+  strerror_s(dest, strlen(strerror(errno)), errno);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char dest[14];
+  // CHECK-FIXES-NEXT: strerror_s(dest, strlen(strerror(errno)) + 1, errno);
+}
+
+void good_strerror_s(int errno) {
+  char dst[14];
+  strerror_s(dst, strlen(strerror(errno)) + 1, errno);
+}
+
+int bad_strncmp_1(char *str0, const char *str1) {
+  return strncmp(str0, str1, (strlen(str0) + 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str0, str1, strlen(str0));
+}
+
+int bad_strncmp_2(char *str2, const char *str3) {
+  return strncmp(str2, str3, 1 + strlen(str2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str2, str3, strlen(str2));
+}
+
+int good_strncmp_1_2(char *str4, const char *str5) {
+  return strncmp(str4, str5, strlen(str4));
+}
+
+int bad_strncmp_3(char *str6) {
+  return strncmp(str6, "string", 7);
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: strncmp(str6, "string", 6);
+}
+
+int good_strncmp_3(char *str7) {
+  return strncmp(str7, "string", 6);
+}
+
+void bad_strxfrm_1(const char *long_source_name) {
+  char long_destination_array_name[13];
+  strxfrm(long_destination_array_name, long_source_name,
+          strlen(long_source_name));
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char long_destination_array_name[14];
+  // CHECK-FIXES-NEXT: strxfrm(long_destination_array_name, long_source_name,
+  // CHECK-FIXES-NEXT: strlen(long_source_name) + 1);
+}
+
+void good_strxfrm_1(const char *long_source_name) {
+  char long_destination_array_name[14];
+  strxfrm(long_destination_array_name, long_source_name,
+          strlen(long_source_name) + 1);
+}
+
+void bad_strxfrm_2() {
+  char long_destination_array_name1[16];
+  strxfrm(long_destination_array_name1, "long_source_name", 16);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: char long_destination_array_name1[17];
+  // CHECK-FIXES: strxfrm(long_destination_array_name1, "long_source_name", 17);
+}
+
+void good_strxfrm_2() {
+  char long_destination_array_name2[17];
+  strxfrm(long_destination_array_name2, "long_source_name", 17);
+}

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp Fri Oct 12 10:22:36 2018
@@ -0,0 +1,131 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c++11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t wcslen(const wchar_t *);
+
+wchar_t *wcschr(const wchar_t *, int);
+errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t);
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);
+size_t wcsxfrm(wchar_t *, const wchar_t *, size_t);
+
+void *wmemchr(const void *, int, size_t);
+void *wmemmove(void *, const void *, size_t);
+errno_t wmemmove_s(void *, size_t, const void *, size_t);
+void *wmemset(void *, int, size_t);
+
+
+void bad_wmemchr_1(wchar_t *position, const wchar_t *src) {
+  position = (wchar_t *)wmemchr(src, L'\0', wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: position = wcschr(src, L'\0');
+}
+
+void good_wmemchr_1(wchar_t *pos, const wchar_t *src) {
+  pos = wcschr(src, L'\0');
+}
+
+void bad_wmemchr_2(wchar_t *position) {
+  position = (wchar_t *)wmemchr(L"foobar", L'\0', 6);
+  // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: position = wcschr(L"foobar", L'\0');
+}
+
+void good_wmemchr_2(wchar_t *pos) {
+  pos = wcschr(L"foobar", L'\0');
+}
+
+
+void bad_wmemmove(const wchar_t *src) {
+  wchar_t dest[13];
+  wmemmove(dest, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest[14];
+  // CHECK-FIXES-NEXT: wmemmove_s(dest, 14, src, wcslen(src) + 1);
+}
+
+void good_wmemmove(const wchar_t *src) {
+  wchar_t dst[14];
+  wmemmove_s(dst, 13, src, wcslen(src) + 1);
+}
+
+void bad_wmemmove_s(wchar_t *dest, const wchar_t *src) {
+  wmemmove_s(dest, 13, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wmemmove_s(dest, 13, src, wcslen(src) + 1);
+}
+
+void good_wmemmove_s_1(wchar_t *dest, const wchar_t *src) {
+  wmemmove_s(dest, 13, src, wcslen(src) + 1);
+}
+
+void bad_wmemset(const wchar_t *src) {
+  wchar_t dest[13];
+  wmemset(dest, L'-', wcslen(src) + 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemset' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wmemset(dest, L'-', wcslen(src));
+}
+
+void good_wmemset(const wchar_t *src) {
+  wchar_t dst[13];
+  wmemset(dst, L'-', wcslen(src));
+}
+
+int bad_wcsncmp_1(wchar_t *wcs0, const wchar_t *wcs1) {
+  return wcsncmp(wcs0, wcs1, (wcslen(wcs0) + 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wcsncmp(wcs0, wcs1, wcslen(wcs0));
+}
+
+int bad_wcsncmp_2(wchar_t *wcs2, const wchar_t *wcs3) {
+  return wcsncmp(wcs2, wcs3, 1 + wcslen(wcs2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wcsncmp(wcs2, wcs3, wcslen(wcs2));
+}
+
+int good_wcsncmp_1_2(wchar_t *wcs4, const wchar_t *wcs5) {
+  return wcsncmp(wcs4, wcs5, wcslen(wcs4));
+}
+
+int bad_wcsncmp_3(wchar_t *wcs6) {
+  return wcsncmp(wcs6, L"string", 7);
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wcsncmp(wcs6, L"string", 6);
+}
+
+int good_wcsncmp_3(wchar_t *wcs7) {
+  return wcsncmp(wcs7, L"string", 6);
+}
+
+void bad_wcsxfrm_1(const wchar_t *long_source_name) {
+  wchar_t long_destination_array_name[13];
+  wcsxfrm(long_destination_array_name, long_source_name,
+          wcslen(long_source_name));
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t long_destination_array_name[14];
+  // CHECK-FIXES-NEXT: wcsxfrm(long_destination_array_name, long_source_name,
+  // CHECK-FIXES-NEXT: wcslen(long_source_name) + 1);
+}
+
+void good_wcsxfrm_1(const wchar_t *long_source_name) {
+  wchar_t long_destination_array_name[14];
+  wcsxfrm(long_destination_array_name, long_source_name,
+          wcslen(long_source_name) + 1);
+}
+
+void bad_wcsxfrm_2() {
+  wchar_t long_destination_array_name1[16];
+  wcsxfrm(long_destination_array_name1, L"long_source_name", 16);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t long_destination_array_name1[17];
+  // CHECK-FIXES: wcsxfrm(long_destination_array_name1, L"long_source_name", 17);
+}
+
+void good_wcsxfrm_2() {
+  wchar_t long_destination_array_name2[17];
+  wcsxfrm(long_destination_array_name2, L"long_source_name", 17);
+}

Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp?rev=344374&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp Fri Oct 12 10:22:36 2018
@@ -0,0 +1,136 @@
+// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
+// RUN: -- -std=c++11
+
+#define __STDC_LIB_EXT1__ 1
+#define __STDC_WANT_LIB_EXT1__ 1
+
+typedef unsigned int size_t;
+typedef int errno_t;
+size_t wcslen(const wchar_t *);
+void *malloc(size_t);
+void *realloc(void *, size_t);
+
+template <size_t size>
+errno_t wcsncpy_s(wchar_t (&dest)[size], const wchar_t *src, size_t length);
+errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t);
+
+template <size_t size>
+wchar_t *wcsncpy(wchar_t (&dest)[size], const wchar_t *src, size_t length);
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);
+
+template <size_t size>
+errno_t wcscpy_s(wchar_t (&dest)[size], const wchar_t *);
+errno_t wcscpy_s(wchar_t *, size_t, const wchar_t *);
+
+template <size_t size>
+wchar_t *wcscpy(wchar_t (&dest)[size], const wchar_t *);
+wchar_t *wcscpy(wchar_t *, const wchar_t *);
+
+errno_t wmemcpy_s(wchar_t *, size_t, const wchar_t *, size_t);
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);
+
+
+//===----------------------------------------------------------------------===//
+// wmemcpy() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_wmemcpy_known_dest(const wchar_t *src) {
+  wchar_t dest01[13];
+  wmemcpy(dest01, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest01[14];
+  // CHECK-FIXES-NEXT: wcscpy_s(dest01, src);
+}
+
+void good_wmemcpy_known_dest(const wchar_t *src) {
+  wchar_t dst01[14];
+  wcscpy_s(dst01, src);
+}
+
+//===----------------------------------------------------------------------===//
+// wmemcpy() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_wmemcpy_full_source_length(const wchar_t *src) {
+  wchar_t dest20[13];
+  wmemcpy(dest20, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest20[14];
+  // CHECK-FIXES-NEXT: wcscpy_s(dest20, src);
+}
+
+void good_wmemcpy_full_source_length(const wchar_t *src) {
+  wchar_t dst20[14];
+  wcscpy_s(dst20, src);
+}
+
+void bad_wmemcpy_partial_source_length(const wchar_t *src) {
+  wchar_t dest21[13];
+  wmemcpy(dest21, src, wcslen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest21[14];
+  // CHECK-FIXES-NEXT: wcsncpy_s(dest21, src, wcslen(src) - 1);
+}
+
+void good_wmemcpy_partial_source_length(const wchar_t *src) {
+  wchar_t dst21[14];
+  wcsncpy_s(dst21, src, wcslen(src) - 1);
+}
+
+//===----------------------------------------------------------------------===//
+// wmemcpy_s() - destination array tests
+//===----------------------------------------------------------------------===//
+
+void bad_wmemcpy_s_unknown_dest(wchar_t *dest40, const wchar_t *src) {
+  wmemcpy_s(dest40, 13, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wcscpy_s(dest40, 13, src);
+}
+
+void good_wmemcpy_s_unknown_dest(wchar_t *dst40, const wchar_t *src) {
+  wcscpy_s(dst40, 13, src);
+}
+
+void bad_wmemcpy_s_known_dest(const wchar_t *src) {
+  wchar_t dest41[13];
+  wmemcpy_s(dest41, 13, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest41[14];
+  // CHECK-FIXES-NEXT: wcscpy_s(dest41, src);
+}
+
+void good_wmemcpy_s_known_dest(const wchar_t *src) {
+  wchar_t dst41[13];
+  wcscpy_s(dst41, src);
+}
+
+//===----------------------------------------------------------------------===//
+// wmemcpy_s() - length tests
+//===----------------------------------------------------------------------===//
+
+void bad_wmemcpy_s_full_source_length(const wchar_t *src) {
+  wchar_t dest60[13];
+  wmemcpy_s(dest60, 13, src, wcslen(src));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest60[14];
+  // CHECK-FIXES-NEXT: wcscpy_s(dest60, src);
+}
+
+void good_wmemcpy_s_full_source_length(const wchar_t *src) {
+  wchar_t dst60[13];
+  wcscpy_s(dst60, src);
+}
+
+void bad_wmemcpy_s_partial_source_length(const wchar_t *src) {
+  wchar_t dest61[13];
+  wmemcpy_s(dest61, 13, src, wcslen(src) - 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result]
+  // CHECK-FIXES: wchar_t dest61[14];
+  // CHECK-FIXES-NEXT: wcsncpy_s(dest61, src, wcslen(src) - 1);
+}
+
+void good_wmemcpy_s_partial_source_length(const wchar_t *src) {
+  wchar_t dst61[13];
+  wcsncpy_s(dst61, src, wcslen(src) - 1);
+}
+




More information about the cfe-commits mailing list