From 379e7ecbc05ad3db3c99999eac67edbdebd012a2 Mon Sep 17 00:00:00 2001 From: Ziqing Luo Date: Thu, 24 Apr 2025 17:03:06 -0700 Subject: [PATCH] [-Wunsafe-buffer-usage] Fix a bug that wrongly assumed CXXMethodDecl always has an identifier (#137248) Fix a bug in UnsafeBufferUsage.cpp that wrongly assumed that CXXMethodDecl always has an identifier. rdar://149071318 (cherry picked from commit be48c0df77413a237565a339c9ccc275b8256631) --- clang/lib/Analysis/UnsafeBufferUsage.cpp | 2 +- clang/test/SemaCXX/bug149071318.cpp | 26 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 clang/test/SemaCXX/bug149071318.cpp diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 16e6be83a7130..95bad8fc25558 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -1350,7 +1350,7 @@ static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx) { const CXXMethodDecl *MD = MCE->getMethodDecl(); const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl(); - if (MD && RD && RD->isInStdNamespace()) + if (MD && RD && RD->isInStdNamespace() && MD->getIdentifier()) if (MD->getName() == "c_str" && RD->getName() == "basic_string") return true; } diff --git a/clang/test/SemaCXX/bug149071318.cpp b/clang/test/SemaCXX/bug149071318.cpp new file mode 100644 index 0000000000000..596d0e238dfba --- /dev/null +++ b/clang/test/SemaCXX/bug149071318.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \ +// RUN: -verify %s + +// This example uncovered a bug in UnsafeBufferUsage.cpp, where the +// code assumed that a CXXMethodDecl always have an identifier. + +int printf( const char* format, char *); // <-- Fake decl of `printf`; to reproduce the bug, this example needs an implicit cast within a printf call. + +namespace std { // fake std namespace; to reproduce the bug, a CXXConversionDecl needs to be in std namespace. + class X { + char * p; + public: + operator char*() {return p;} + }; + + class Y { + public: + X x; + }; + +} + +void test(std::Y &y) { + // Here `y.x` involves an implicit cast and calls the overloaded cast operator, which has no identifier: + printf("%s", y.x); // expected-warning{{function 'printf' is unsafe}} expected-note{{}} +}