llvm-project/clang-tools-extra/test/clang-tidy/bugprone-not-null-terminate...

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

112 lines
4.3 KiB
C++
Raw Normal View History

[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
2019-10-13 16:28:27 +08:00
// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \
// RUN: -- -std=c++11 -I %S/Inputs/bugprone-not-null-terminated-result
// FIXME: Something wrong with the APInt un/signed conversion on Windows:
// in 'wcsncmp(wcs6, L"string", 7);' it tries to inject '4294967302' as length.
// UNSUPPORTED: system-windows
[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
2019-10-13 16:28:27 +08:00
#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);
}