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

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

113 lines
3.5 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=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);
}