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

via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 12 14:06:15 PDT 2018


Hi Csaba and Jonas,

This commit is causing a bot build failure due to recursive template instantiation exceeding the maximum depth of 256. Can you take a look?

http://lab.llvm.org:8011/builders/clang-cmake-armv8-quick/builds/7438

FAILED: tools/clang/tools/extra/clang-tidy/bugprone/CMakeFiles/clangTidyBugproneModule.dir/NotNullTerminatedResultCheck.cpp.o 
/usr/local/bin/c++   -DGTEST_HAS_RTTI=0 -D_DEBUG -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/clang/tools/extra/clang-tidy/bugprone -I/home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone -I/home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/include -Itools/clang/include -I/usr/include/libxml2 -Iinclude -I/home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/include -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wstring-conversion -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -Wno-nested-anon-types -O3    -UNDEBUG  -fno-exceptions -fno-rtti -MMD -MT tools/clang/tools/extra/clang-tidy/bugprone/CMakeFiles/clangTidyBugproneModule.dir/NotNullTerminatedResultCheck.cpp.o -MF tools/clang/tools/extra/clang-tidy/bugprone/CMakeFiles/clangTidyBugproneModule.dir/NotNullTerminatedResultCheck.cpp.o.d -o tools/clang/tools/extra/clang-tidy/bugprone/CMakeFiles/clangTidyBugproneModule.dir/NotNullTerminatedResultCheck.cpp.o -c /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp:10:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/NotNullTerminatedResultCheck.h:13:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/../ClangTidy.h:13:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/../ClangTidyDiagnosticConsumer.h:13:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/tools/clang/tools/extra/clang-tidy/bugprone/../ClangTidyOptions.h:14:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/include/llvm/ADT/Optional.h:22:
In file included from /home/buildslave/buildslave/clang-cmake-armv8-quick/llvm/include/llvm/Support/type_traits.h:18:
/usr/bin/../lib/gcc/arm-linux-gnueabihf/5.4.0/../../../../include/c++/5.4.0/type_traits:741:43: fatal error: recursive template instantiation exceeded maximum depth of 256
    : public __and_<is_array<_Tp>, __not_<extent<_Tp>>>::type
                                          ^~~~~~~~~~~~
/usr/bin/../lib/gcc/arm-linux-gnueabihf/5.4.0/../../../../include/c++/5.4.0/type_traits:115:26: note: in instantiation of template class 'std::__is_array_unknown_bounds<std::_Tuple_impl<17, clang::ast_matchers::internal::VariadicOperatorMatcher<clang::ast_matchers::internal::Matcher<clang::CallExpr>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)> > &, clang::ast_matchers::internal::VariadicOperatorMatcher<clang::ast_matchers::internal::Matcher<clang::CallExpr>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)> > &> >' requested here
    : public conditional<_B1::value, _B1, _B2>::type
                         ^
/usr/bin/../lib/gcc/arm-linux-gnueabihf/5.4.0/../../../../include/c++/5.4.0/type_traits:120:14: note: in instantiation of template class 'std::__or_<std::__is_array_unknown_bounds<std::_Tuple_impl<17, clang::ast_matchers::internal::VariadicOperatorMatcher<clang::ast_matchers::internal::Matcher<clang::CallExpr>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, clang::ast_matchers::internal::PolymorphicMatcherWithParam2<internal::matcher_hasArgument0Matcher, unsigned int, clang::ast_matchers::internal::Matcher<clang::Expr>, void (clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::ObjCMessageExpr>)>, 

<many more similar lines snipped>

Douglas Yung

> -----Original Message-----
> From: cfe-commits [mailto:cfe-commits-bounces at lists.llvm.org] On Behalf
> Of Jonas Toth via cfe-commits
> Sent: Friday, October 12, 2018 10:23
> To: cfe-commits at lists.llvm.org
> Subject: [clang-tools-extra] r344374 - [clang-tidy] New checker for not
> null-terminated result caused by strlen(), size() or equal length
> 
> 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&vie
> w=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::NarrowingConversionsChe
> ck>(
>          "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=d
> iff
> =======================================================================
> =======
> --- 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);
> +}
> +
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


More information about the cfe-commits mailing list