llvm-project/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
Ziqing Luo 0abf4975bb
[-Wunsafe-buffer-usage] Do not warn about class methods with libc function names (#151270)
This commit fixes the false positive that C++ class methods with libc
function names would be false warned about. For example,

```
struct T {void strcpy() const;};
void test(const T& t) {  str.strcpy(); // no warn }
```

rdar://156264388
2025-08-07 14:31:13 +08:00

251 lines
11 KiB
C++

// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
// RUN: -verify %s
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
// RUN: -verify %s -x objective-c++
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
// RUN: -verify %s
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
// RUN: -verify %s -DTEST_STD_NS
typedef struct {} FILE;
typedef unsigned int size_t;
#ifdef TEST_STD_NS
namespace std {
#endif
void memcpy();
void __asan_memcpy();
void strcpy();
void strcpy_s();
void wcscpy_s();
unsigned strlen( const char* str );
int fprintf( FILE* stream, const char* format, ... );
int printf( const char* format, ... );
int sprintf( char* buffer, const char* format, ... );
int swprintf( char* buffer, const char* format, ... );
int snprintf( char* buffer, unsigned buf_size, const char* format, ... );
int snwprintf( char* buffer, unsigned buf_size, const char* format, ... );
int snwprintf_s( char* buffer, unsigned buf_size, const char* format, ... );
int vsnprintf( char* buffer, unsigned buf_size, const char* format, ... );
int sscanf_s(const char * buffer, const char * format, ...);
int sscanf(const char * buffer, const char * format, ... );
int wprintf(const wchar_t* format, ... );
int __asan_printf();
#ifdef TEST_STD_NS
} //namespace std
using namespace std;
#endif
namespace std {
template< class InputIt, class OutputIt >
OutputIt copy( InputIt first, InputIt last,
OutputIt d_first );
struct iterator{};
template<typename T>
struct span {
T * ptr;
T * data();
unsigned size_bytes();
unsigned size();
iterator begin() const noexcept;
iterator end() const noexcept;
};
template<typename T>
struct basic_string {
T* p;
T *c_str();
T *data();
unsigned size_bytes();
unsigned size();
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
template<typename T>
struct basic_string_view {
T *c_str() const noexcept;
T *data() const noexcept;
unsigned size();
const T* begin() const noexcept;
const T* end() const noexcept;
};
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
}
void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
typedef FILE * _Nullable aligned_file_ptr_t __attribute__((align_value(64)));
typedef char * _Nullable aligned_char_ptr_t __attribute__((align_value(64)));
aligned_file_ptr_t fp;
aligned_char_ptr_t cp;
memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
__builtin_memcpy(p, q, 64); // expected-warning{{function '__builtin_memcpy' is unsafe}}
__builtin___memcpy_chk(p, q, 8, 64); // expected-warning{{function '__builtin___memcpy_chk' is unsafe}}
__asan_memcpy(); // expected-warning{{function '__asan_memcpy' is unsafe}}
strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
strcpy_s(); // expected-warning{{function 'strcpy_s' is unsafe}}
wcscpy_s(); // expected-warning{{function 'wcscpy_s' is unsafe}}
#ifdef TEST_STD_NS
std::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
std::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
#endif
/* Test printfs */
fprintf((FILE*)p, "%s%d", p, *p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
printf("%s%d", // expected-warning{{function 'printf' is unsafe}}
p, // expected-note{{string argument is not guaranteed to be null-terminated}} note attached to the unsafe argument
*p);
printf(cp, p, *p); // expected-warning{{function 'printf' is unsafe}} // expected-note{{string argument is not guaranteed to be null-terminated}}
sprintf(q, "%s%d", "hello", *p); // expected-warning{{function 'sprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
swprintf(q, "%s%d", "hello", *p); // expected-warning{{function 'swprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
snprintf(q, 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
snprintf(cp, 10, "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
snprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
snwprintf(s.data(), s2.size(), "%s%d", "hello", *p); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
snwprintf_s( // expected-warning{{function 'snwprintf_s' is unsafe}}
s.data(), // expected-note{{buffer pointer and size may not match}} // note attached to the buffer
s2.size(),
"%s%d", "hello", *p);
vsnprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // expected-warning{{function 'vsnprintf' is unsafe}} expected-note{{'va_list' is unsafe}}
sscanf(p, "%s%d", "hello", *p); // expected-warning{{function 'sscanf' is unsafe}}
sscanf_s(p, "%s%d", "hello", *p); // expected-warning{{function 'sscanf_s' is unsafe}}
fprintf((FILE*)p, "%P%d%p%i hello world %32s", *p, *p, p, *p, p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
fprintf(fp, "%P%d%p%i hello world %32s", *p, *p, p, *p, p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
wprintf(L"hello %s", p); // expected-warning{{function 'wprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
char a[10], b[11];
int c[10];
std::wstring WS;
snprintf(a, sizeof(b), "%s", __PRETTY_FUNCTION__); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
snprintf((char*)c, sizeof(c), "%s", __PRETTY_FUNCTION__); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
fprintf((FILE*)p, "%P%d%p%i hello world %32s", *p, *p, p, *p, "hello"); // no warn
fprintf(fp, "%P%d%p%i hello world %32s", *p, *p, p, *p, "hello"); // no warn
printf("%s%d", "hello", *p); // no warn
snprintf(s.data(), s.size_bytes(), "%s%d", "hello", *p); // no warn
snprintf(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
snwprintf(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
snwprintf_s(s.data(), s.size_bytes(), "%s%d", __PRETTY_FUNCTION__, *p); // no warn
wprintf(L"hello %ls", L"world"); // no warn
wprintf(L"hello %ls", WS.c_str()); // no warn
strlen("hello");// no warn
__asan_printf();// a printf but no argument, so no warn
}
void safe_examples(std::string s1, int *p) {
snprintf(s1.data(), s1.size_bytes(), "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
snprintf(s1.data(), s1.size_bytes(), s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
printf("%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
printf(s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
fprintf((FILE*)0, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", p, s1.c_str()); // no warn
fprintf((FILE*)0, s1.c_str(), __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
char a[10];
char c = 'c';
snprintf(a, sizeof a, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
snprintf(a, sizeof(decltype(a)), "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
snprintf(a, 10, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
snprintf(&c, 1, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
snprintf(nullptr, 0, "%s%d%s%p%s", __PRETTY_FUNCTION__, *p, "hello", s1.c_str()); // no warn
}
void test_sarg_precision(std::string Str, std::string_view Sv, std::wstring_view WSv,
std::span<char> SpC, std::span<int> SpI) {
printf("%.*s");
printf("%.*s", (int)Str.size(), Str.data());
printf("%.*s", (int)Str.size_bytes(), Str.data());
printf("%.*s", (int)Sv.size(), Sv.data());
printf("%.*s", (int)SpC.size(), SpC.data());
printf("%.*s", SpC.size(), SpC.data());
printf("%.*ls", WSv.size(), WSv.data());
printf("%.*s", SpC.data()); // no warn because `SpC.data()` is passed to the precision while the actually string pointer is not given
printf("%.*s", SpI.size(), SpI.data()); // expected-warning {{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
printf("%.*s", SpI.size(), SpC.data()); // expected-warning {{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
printf("%.*s", WSv.size(), WSv.data()); // expected-warning {{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
char a[10];
int b[10];
printf("%.10s", a);
printf("%.11s", a); // expected-warning {{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
printf("%.10s", b); // expected-warning {{function 'printf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
}
void g(char *begin, char *end, char *p, std::span<char> s) {
std::copy(begin, end, p); // no warn
std::copy(s.begin(), s.end(), s.begin()); // no warn
}
// warning gets turned off
void ff(char * p, char * q, std::span<char> s, std::span<char> s2) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
memcpy();
__builtin_memcpy(p, q, 64);
__builtin___memcpy_chk(p, q, 8, 64);
__asan_memcpy();
strcpy();
#ifdef TEST_STD_NS
std::strcpy();
std::memcpy();
#endif
strcpy_s();
wcscpy_s();
#pragma clang diagnostic pop
}
// functions not in global scope or std:: namespace are not libc
// functions regardless of their names:
struct StrBuff
{
void strcpy();
void strcpy(char* dst);
void memcpy(void *dst, const void *src, size_t size);
};
namespace NS {
void strcpy();
void strcpy(char* dst);
void memcpy(void *dst, const void *src, size_t size);
}
namespace std {
// class methods even in std namespace cannot be libc functions:
struct LibC
{
void strcpy();
void strcpy(char* dst);
void memcpy(void *dst, const void *src, size_t size);
};
}
void test(StrBuff& str)
{
char buff[64];
str.strcpy();
str.strcpy(buff);
str.memcpy(buff, buff, 64);
NS::strcpy();
NS::strcpy(buff);
NS::memcpy(buff, buff, 64);
std::LibC LibC;
LibC.strcpy();
LibC.strcpy(buff);
LibC.memcpy(buff, buff, 64);
}