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

Summary:
New checker called bugprone-not-null-terminated-result. This checker 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 and their respective `wchar_t` based functions are checked:

`memcpy`, `memcpy_s`, `memchr`, `memmove`, `memmove_s`, `strerror_s`,
`strncmp`, `strxfrm`

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 not enough to hold the
null terminator.

```
    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.
It also tries to adjust the capacity of the destination array:

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

Note: It cannot guarantee to rewrite every of the path-sensitive memory
allocations.

Reviewed By: JonasToth, aaron.ballman, whisperity, alexfh

Tags: #clang-tools-extra, #clang

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

llvm-svn: 374707
This commit is contained in:
Csaba Dabis 2019-10-13 08:28:27 +00:00
parent d50cb9ac8c
commit 82f8f8b44c
17 changed files with 2168 additions and 0 deletions

View File

@ -32,6 +32,7 @@
#include "MisplacedWideningCastCheck.h"
#include "MoveForwardingReferenceCheck.h"
#include "MultipleStatementMacroCheck.h"
#include "NotNullTerminatedResultCheck.h"
#include "ParentVirtualCallCheck.h"
#include "PosixReturnCheck.h"
#include "SizeofContainerCheck.h"
@ -109,6 +110,8 @@ public:
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
"bugprone-narrowing-conversions");
CheckFactories.registerCheck<NotNullTerminatedResultCheck>(
"bugprone-not-null-terminated-result");
CheckFactories.registerCheck<ParentVirtualCallCheck>(
"bugprone-parent-virtual-call");
CheckFactories.registerCheck<PosixReturnCheck>(

View File

@ -24,6 +24,7 @@ add_clang_library(clangTidyBugproneModule
MisplacedWideningCastCheck.cpp
MoveForwardingReferenceCheck.cpp
MultipleStatementMacroCheck.cpp
NotNullTerminatedResultCheck.cpp
ParentVirtualCallCheck.cpp
PosixReturnCheck.cpp
SizeofContainerCheck.cpp

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
//===--- NotNullTerminatedResultCheck.h - clang-tidy ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) 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

View File

@ -79,6 +79,15 @@ Improvements to clang-tidy
Finds obvious infinite loops (loops where the condition variable is not
changed at all).
- 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(str) + 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:`cppcoreguidelines-init-variables
<clang-tidy/checks/cppcoreguidelines-init-variables>` check.

View File

@ -0,0 +1,127 @@
.. 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 and their respective ``wchar_t`` based functions are checked:
``memcpy``, ``memcpy_s``, ``memchr``, ``memmove``, ``memmove_s``,
``strerror_s``, ``strncmp``, ``strxfrm``
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 not enough to hold the null terminator.
.. 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. It also
tries to adjust the capacity of the destination array:
.. 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;
}
Note: It cannot guarantee to rewrite every of the path-sensitive memory
allocations.
.. _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.
It rewrites the ``wchar_t`` based memory handler functions respectively.
Rewrite based on the destination array
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- If copy to the destination array cannot overflow [1] 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 [1] 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 and
the destination array is plain ``char``/``wchar_t`` without ``un/signed`` then
the length of the destination array can be omitted.
- If the new function is could be safe version and the destination array is
``un/signed`` it needs to be casted to plain ``char *``/``wchar_t *``.
[1] It is possible to overflow:
- If the capacity of the destination array is unknown.
- If the given length is equal to the destination array's 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 (ending with ``cpy_s``).
- Otherwise we assume that the programmer wanted to copy 'N' characters, so the
new function is ``ncpy``-like which copies 'N' characters.
Transformations with 'strlen()' or equal length of this expression
------------------------------------------------------------------
It transforms the ``wchar_t`` based memory and string handler functions
respectively (where only ``strerror_s`` does not have ``wchar_t`` based alias).
Memory handler functions
^^^^^^^^^^^^^^^^^^^^^^^^
- ``memcpy``: Visit 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.
- The given length is going to be removed.
- ``memmove``:
- If safe functions are available the new function is ``memmove_s``, which has
a new second argument which is the length of the destination array, it is
adjusted, and the length of the source string 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.
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`` 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 versions (e.g. 'memcpy_s()'). The default value is ``1``.

View File

@ -60,6 +60,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-posix-return
bugprone-sizeof-container

View File

@ -0,0 +1,39 @@
//===- not-null-terminated-result-c.h - Helper header -------------*- C -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header helps to maintain every function call checked by the
// NotNullTerminatedResult checker.
//
//===----------------------------------------------------------------------===//
#pragma clang system_header
typedef unsigned int size_t;
typedef int errno_t;
size_t strlen(const char *str);
void *malloc(size_t size);
char *strerror(int errnum);
errno_t strerror_s(char *buffer, size_t bufferSize, int errnum);
char *strcpy(char *dest, const char *src);
errno_t strcpy_s(char *dest, size_t destSize, const char *src);
char *strncpy(char *dest, const char *src, size_t count);
errno_t strncpy_s(char *dest, size_t destSize, const char *src, size_t count);
void *memcpy(void *dest, const void *src, size_t count);
errno_t memcpy_s(void *dest, size_t destSize, const void *src, size_t count);
char *strchr(char *str, int c);
int strncmp(const char *str1, const char *str2, size_t count);
size_t strxfrm(char *dest, const char *src, size_t count);
void *memchr(const void *buffer, int c, size_t count);
void *memmove(void *dest, const void *src, size_t count);
errno_t memmove_s(void *dest, size_t destSize, const void *src, size_t count);
void *memset(void *dest, int c, size_t count);

View File

@ -0,0 +1,65 @@
//===- not-null-terminated-result-cxx.h - Helper header ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header helps to maintain every function call checked by the
// NotNullTerminatedResult checker.
//
//===----------------------------------------------------------------------===//
#pragma clang system_header
#include "not-null-terminated-result-c.h"
namespace std {
template <typename T>
struct basic_string {
basic_string();
const T *data() const;
unsigned long size() const;
unsigned long length() const;
};
typedef basic_string<char> string;
} // namespace std
size_t wcslen(const wchar_t *str);
template <size_t size>
char *strcpy(char (&dest)[size], const char *src);
template <size_t size>
wchar_t *wcscpy(wchar_t (&dest)[size], const wchar_t *src);
wchar_t *wcscpy(wchar_t *dest, const wchar_t *src);
template <size_t size>
errno_t strcpy_s(char (&dest)[size], const char *src);
template <size_t size>
errno_t wcscpy_s(wchar_t (&dest)[size], const wchar_t *src);
errno_t wcscpy_s(wchar_t *dest, size_t destSize, const wchar_t *src);
template <size_t size>
char *strncpy(char (&dest)[size], const char *src, size_t count);
template <size_t size>
wchar_t *wcsncpy(wchar_t (&dest)[size], const wchar_t *src, size_t count);
wchar_t *wcsncpy(wchar_t *dest, const wchar_t *src, size_t count);
template <size_t size>
errno_t strncpy_s(char (&dest)[size], const char *src, size_t count);
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 *dest, size_t destSize, const wchar_t *src, size_t c);
wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t count);
errno_t wmemcpy_s(wchar_t *dest, size_t destSize, const wchar_t *src, size_t c);
wchar_t *wcschr(const wchar_t *str, int c);
int wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t count);
size_t wcsxfrm(wchar_t *dest, const wchar_t *src, size_t count);
void *wmemchr(const void *buffer, int c, size_t count);
void *wmemmove(void *dest, const void *src, size_t count);
errno_t wmemmove_s(void *dest, size_t destSize, const void *src, size_t count);
void *wmemset(void *dest, int c, size_t count);

View File

@ -0,0 +1,84 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-c.h"
void path_sensitive_unknown_length(char *position, const char *src) {
int length;
length = strlen(src);
position = (char *)memchr(src, '\0', length);
}
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_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);
}

View File

@ -0,0 +1,71 @@
// 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 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-c.h"
// The following is not defined therefore the safe functions are unavailable.
// #define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
//===----------------------------------------------------------------------===//
// 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: strcpy((char *)dest00, src);
}
void good_memcpy_not_just_char_dest(const char *src) {
unsigned char dst00[14];
strcpy((char *)dst00, src);
}
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';
}

View File

@ -0,0 +1,124 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c++11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-cxx.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
//===----------------------------------------------------------------------===//
// 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: strcpy_s((char *)dest00, 14, src);
}
void good_memcpy_not_just_char_dest(const char *src) {
unsigned char dst00[14];
strcpy_s((char *)dst00, 14, src);
}
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 = 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);
}

View File

@ -0,0 +1,112 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-c.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
#define SRC_LENGTH 3
#define SRC "foo"
//===----------------------------------------------------------------------===//
// False positive suppression.
//===----------------------------------------------------------------------===//
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);
}
//===----------------------------------------------------------------------===//
// Special cases.
//===----------------------------------------------------------------------===//
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() {
char dest05[SRC_LENGTH];
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: char dest05[SRC_LENGTH + 1];
// CHECK-FIXES-NEXT: strcpy(dest05, SRC);
}
void good_memcpy_macro() {
char dst05[SRC_LENGTH + 1];
strcpy(dst05, SRC);
}

View File

@ -0,0 +1,124 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-c.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
//===----------------------------------------------------------------------===//
// 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: strcpy_s((char *)dest00, 14, src);
}
void good_memcpy_not_just_char_dest(const char *src) {
unsigned char dst00[14];
strcpy_s((char *)dst00, 14, src);
}
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);
}

View File

@ -0,0 +1,118 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-c.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
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_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);
}

View File

@ -0,0 +1,106 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c++11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-cxx.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
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);
}
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);
}

View File

@ -0,0 +1,111 @@
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c++11 -I %S/Inputs/bugprone-not-null-terminated-result
#include "not-null-terminated-result-cxx.h"
#define __STDC_LIB_EXT1__ 1
#define __STDC_WANT_LIB_EXT1__ 1
//===----------------------------------------------------------------------===//
// 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);
}