forked from OSchip/llvm-project
[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:
parent
d50cb9ac8c
commit
82f8f8b44c
|
@ -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>(
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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``.
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue