[clang] [-Wunsafe-buffer-usage] Do not warn about class methods with libc function names (PR #151270)
Ziqing Luo via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 1 01:03:44 PDT 2025
https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/151270
>From 50ea6041f13cdc4c9d4ae475d11b970f1e026fac Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing at udel.edu>
Date: Wed, 30 Jul 2025 11:23:32 +0800
Subject: [PATCH 1/2] [-Wunsafe-buffer-usage] Do not warn about class methods
with libc function names
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
---
clang/lib/Analysis/UnsafeBufferUsage.cpp | 2 +-
...warn-unsafe-buffer-usage-libc-functions.cpp | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index ac47b12cc8d72..6cd71c7674a92 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1925,7 +1925,7 @@ class UnsafeLibcFunctionCallGadget : public WarningGadget {
if (!CE || !CE->getDirectCallee())
return false;
const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
- if (!FD)
+ if (!FD || isa<CXXMethodDecl>(FD))
return false;
auto isSingleStringLiteralArg = false;
if (CE->getNumArgs() == 1) {
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
index d6c32ca7baa5d..e6902c5a95dc0 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
@@ -155,3 +155,21 @@ void ff(char * p, char * q, std::span<char> s, std::span<char> s2) {
wcscpy_s();
#pragma clang diagnostic pop
}
+
+
+namespace CXXMethodNoWarn {
+ struct StrBuff
+ {
+ void strcpy() const;
+ void strcpy(char* dst) const;
+ void Strcpy(char* dst) const;
+ };
+
+ void test(const StrBuff& str)
+ {
+ char buff[64];
+ str.strcpy();
+ str.strcpy(buff);
+ str.Strcpy(buff);
+ }
+} // namespace CXXMethodNoWarn
>From b1b87fbeb7c90214bbd3ba827e6d8c858e39a12e Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing at udel.edu>
Date: Fri, 1 Aug 2025 15:56:41 +0800
Subject: [PATCH 2/2] address comments
---
clang/lib/Analysis/UnsafeBufferUsage.cpp | 10 ++-
...arn-unsafe-buffer-usage-libc-functions.cpp | 79 ++++++++++++++-----
2 files changed, 68 insertions(+), 21 deletions(-)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 6cd71c7674a92..0ef4d8e8ec86e 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1925,7 +1925,15 @@ class UnsafeLibcFunctionCallGadget : public WarningGadget {
if (!CE || !CE->getDirectCallee())
return false;
const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
- if (!FD || isa<CXXMethodDecl>(FD))
+ if (!FD)
+ return false;
+
+ bool IsGlobalAndNotInAnyNamespace =
+ FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
+
+ // A libc function must either be in the std:: namespace or a global
+ // function that is not in any namespace:
+ if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
return false;
auto isSingleStringLiteralArg = false;
if (CE->getNumArgs() == 1) {
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
index e6902c5a95dc0..5b0d494a6575b 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
@@ -4,8 +4,16 @@
// 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();
@@ -25,6 +33,11 @@ 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,
@@ -51,10 +64,6 @@ namespace std {
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
-
- // C function under std:
- void memcpy();
- void strcpy();
}
void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
@@ -64,14 +73,16 @@ void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
aligned_char_ptr_t cp;
memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
- std::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}}
- std::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}}
@@ -145,31 +156,59 @@ 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();
- std::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
}
-namespace CXXMethodNoWarn {
- struct StrBuff
+
+// 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() const;
- void strcpy(char* dst) const;
- void Strcpy(char* dst) const;
+ void strcpy();
+ void strcpy(char* dst);
+ void memcpy(void *dst, const void *src, size_t size);
};
+}
- void test(const StrBuff& str)
- {
- char buff[64];
- str.strcpy();
- str.strcpy(buff);
- str.Strcpy(buff);
- }
-} // namespace CXXMethodNoWarn
+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);
+}
More information about the cfe-commits
mailing list