https://github.com/zmodem updated https://github.com/llvm/llvm-project/pull/175749
>From f19cd75675df48320fab4a9131ac535eb5f9de93 Mon Sep 17 00:00:00 2001 From: Hans Wennborg <[email protected]> Date: Tue, 13 Jan 2026 11:21:44 +0100 Subject: [PATCH 1/3] [-Wunsafe-buffer-usage] Separate flag for format-attributed functions PR #173096 extended -Wunsafe-buffer-usage-in-libc-call to apply to all functions with the 'format' attribute. This change moves those warnings behind a separate -Wunsafe-buffer-usage-format-attr flag (implicitly enabled by -Wunsafe-buffer-usage), allowing projects to decide whether they want to opt in to this or not. --- .../Analysis/Analyses/UnsafeBufferUsage.h | 3 +- clang/include/clang/Basic/DiagnosticGroups.td | 3 +- .../clang/Basic/DiagnosticSemaKinds.td | 4 +++ clang/lib/Analysis/UnsafeBufferUsage.cpp | 12 +++++-- clang/lib/Sema/AnalysisBasedWarnings.cpp | 7 +++- ...arn-unsafe-buffer-usage-libc-functions.cpp | 34 +++++++++---------- 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index ea41eb3becfcf..65dbd3b2ab9d1 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -117,8 +117,9 @@ class UnsafeBufferUsageHandler { /// safe pattern; /// is 3 if string arguments do not guarantee null-termination /// is 4 if the callee takes va_list + /// has bit 3 (0x8) set if the callee is a function with the format attribute /// \param UnsafeArg one of the actual arguments that is unsafe, non-null - /// only when `2 <= PrintfInfo <= 3` + /// only when `2 <= PrintfInfo <= 3 (ignoring the "format attribute" bit)` virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg = nullptr) = 0; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 3764475fbd3df..08ad2fc4b954d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1773,8 +1773,9 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">; // Warnings and fixes to support the "safe buffers" programming model. def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">; def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; +def UnsafeBufferUsageFormatAttr : DiagGroup<"unsafe-buffer-usage-format-attr">; def UnsafeBufferUsageInUniquePtrArrayAccess : DiagGroup<"unsafe-buffer-usage-in-unique-ptr-array-access">; -def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess]>; +def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageFormatAttr, UnsafeBufferUsageInUniquePtrArrayAccess]>; // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5cbbc7d130c99..967d54bc44979 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13450,6 +13450,10 @@ def warn_unsafe_buffer_operation : Warning< def warn_unsafe_buffer_libc_call : Warning< "function %0 is unsafe">, InGroup<UnsafeBufferUsageInLibcCall>, DefaultIgnore; +def warn_unsafe_buffer_format_attr_call : Warning< + "formatting function %0 is unsafe">, + InGroup<UnsafeBufferUsageFormatAttr>, DefaultIgnore; + def note_unsafe_buffer_printf_call : Note< "%select{|change to 'snprintf' for explicit bounds checking | buffer pointer and size may not match" "|string argument is not guaranteed to be null-terminated" diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index c7a5988445aa1..12af536053581 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -2098,6 +2098,7 @@ class UnsafeLibcFunctionCallGadget : public WarningGadget { // guarantee null-termination VA_LIST = 4, // one of the `-printf`s function that take va_list, which is // considered unsafe as it is not compile-time check + FORMAT_ATTR = 8, // flag: the callee has the format attribute } WarnedFunKind = OTHERS; UnsafeLibcFunctionCallGadget(const MatchResult &Result) @@ -2282,11 +2283,16 @@ class UnsafeFormatAttributedFunctionCallGadget : public WarningGadget { ASTContext &Ctx) const override { if (UnsafeArg) Handler.handleUnsafeLibcCall( - Call, UnsafeLibcFunctionCallGadget::UnsafeKind::STRING, Ctx, - UnsafeArg); + Call, + UnsafeLibcFunctionCallGadget::UnsafeKind::STRING | + UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR, + Ctx, UnsafeArg); else Handler.handleUnsafeLibcCall( - Call, UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS, Ctx); + Call, + UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS | + UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR, + Ctx); } DeclUseList getClaimedVarUseSites() const override { return {}; } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 7b08648080710..df7488c3059e7 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2523,7 +2523,12 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg = nullptr) override { - S.Diag(Call->getBeginLoc(), diag::warn_unsafe_buffer_libc_call) + unsigned DiagID = diag::warn_unsafe_buffer_libc_call; + if (PrintfInfo & 0x8) { + DiagID = diag::warn_unsafe_buffer_format_attr_call; + PrintfInfo ^= 0x8; + } + S.Diag(Call->getBeginLoc(), DiagID) << Call->getDirectCallee() // We've checked there is a direct callee << Call->getSourceRange(); if (PrintfInfo > 0) { 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 fe9bc7c809c96..7d6b118154bec 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp @@ -2,10 +2,10 @@ // RUN: -verify %s // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -Wno-gcc-compat\ // RUN: -verify %s -x objective-c++ -// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call -Wno-gcc-compat\ -// RUN: -verify %s -// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call -Wno-gcc-compat\ -// RUN: -verify %s -DTEST_STD_NS +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \ +// RUN: -Wunsafe-buffer-usage-format-attr -Wno-gcc-compat -verify %s +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \ +// RUN: -Wunsafe-buffer-usage-format-attr -Wno-gcc-compat -verify %s -DTEST_STD_NS typedef struct {} FILE; typedef unsigned int size_t; @@ -295,7 +295,7 @@ struct FormatAttrTestMember2 { void test_format_attr(char * Str, std::string StdStr) { myprintf("hello", Str); myprintf("hello %s", StdStr.c_str()); - myprintf("hello %s", Str); // expected-warning{{function 'myprintf' is unsafe}} \ + myprintf("hello %s", Str); // expected-warning{{formatting function 'myprintf' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} extern int errno; @@ -304,12 +304,12 @@ void test_format_attr(char * Str, std::string StdStr) { myprintf_2("hello", 0, Str); myprintf_2("hello %s", 0, StdStr.c_str()); - myprintf_2("hello %s", 0, Str); // expected-warning{{function 'myprintf_2' is unsafe}} \ + myprintf_2("hello %s", 0, Str); // expected-warning{{formatting function 'myprintf_2' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} myprintf_3("irrelevant", "hello", 0, Str); myprintf_3("irrelevant", "hello %s", 0, StdStr.c_str()); - myprintf_3("irrelevant", "hello %s", 0, Str); // expected-warning{{function 'myprintf_3' is unsafe}} \ + myprintf_3("irrelevant", "hello %s", 0, Str); // expected-warning{{formatting function 'myprintf_3' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} myscanf("hello %s"); myscanf("hello %s", Str); // expected-warning{{function 'myscanf' is unsafe}} @@ -325,30 +325,30 @@ void test_format_attr(char * Str, std::string StdStr) { Obj.myprintf("hello", Str); Obj.myprintf("hello %s", StdStr.c_str()); - Obj.myprintf("hello %s", Str); // expected-warning{{function 'myprintf' is unsafe}} \ + Obj.myprintf("hello %s", Str); // expected-warning{{formatting function 'myprintf' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} Obj.myprintf_2("hello", 0, Str); Obj.myprintf_2("hello %s", 0, StdStr.c_str()); - Obj.myprintf_2("hello %s", 0, Str); // expected-warning{{function 'myprintf_2' is unsafe}} \ + Obj.myprintf_2("hello %s", 0, Str); // expected-warning{{formatting function 'myprintf_2' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} Obj.myprintf_3("irrelevant", "hello", 0, Str); Obj.myprintf_3("irrelevant", "hello %s", 0, StdStr.c_str()); - Obj.myprintf_3("irrelevant", "hello %s", 0, Str); // expected-warning{{function 'myprintf_3' is unsafe}} \ + Obj.myprintf_3("irrelevant", "hello %s", 0, Str); // expected-warning{{formatting function 'myprintf_3' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} Obj.myscanf("hello %s"); - Obj.myscanf("hello %s", Str); // expected-warning{{function 'myscanf' is unsafe}} + Obj.myscanf("hello %s", Str); // expected-warning{{formatting function 'myscanf' is unsafe}} - Obj.myscanf("hello %d", &X); // expected-warning{{function 'myscanf' is unsafe}} + Obj.myscanf("hello %d", &X); // expected-warning{{formatting function 'myscanf' is unsafe}} - Obj.myprintf_default("irrelevant"); // expected-warning{{function 'myprintf_default' is unsafe}} + Obj.myprintf_default("irrelevant"); // expected-warning{{formatting function 'myprintf_default' is unsafe}} // expected-note@*{{string argument is not guaranteed to be null-terminated}} Obj("hello", Str); Obj("hello %s", StdStr.c_str()); - Obj("hello %s", Str); // expected-warning{{function 'operator()' is unsafe}} \ + Obj("hello %s", Str); // expected-warning{{formatting function 'operator()' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} Obj["hello"]; Obj["hello %s"]; @@ -357,7 +357,7 @@ void test_format_attr(char * Str, std::string StdStr) { Obj2("hello", Str); Obj2("hello %s", StdStr.c_str()); - Obj2("hello %s", Str); // expected-warning{{function 'operator()' is unsafe}} \ + Obj2("hello %s", Str); // expected-warning{{formatting function 'operator()' is unsafe}} \ expected-note{{string argument is not guaranteed to be null-terminated}} } @@ -367,9 +367,9 @@ void myprintf_arg_idx_oob(const char *) __attribute__((__format__ (__printf__, 1 void test_format_attr_invalid_arg_idx(char * Str, std::string StdStr) { myprintf_arg_idx_oob("hello"); - myprintf_arg_idx_oob(Str); // expected-warning{{function 'myprintf_arg_idx_oob' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} + myprintf_arg_idx_oob(Str); // expected-warning{{formatting function 'myprintf_arg_idx_oob' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} myprintf_arg_idx_oob(StdStr.c_str()); myprintf("hello"); - myprintf(Str); // expected-warning{{function 'myprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} + myprintf(Str); // expected-warning{{formatting function 'myprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}} myprintf(StdStr.c_str()); } >From 5cec0940987ff2df1a51acddc56534f9569e4492 Mon Sep 17 00:00:00 2001 From: Hans Wennborg <[email protected]> Date: Mon, 19 Jan 2026 10:16:58 +0100 Subject: [PATCH 2/3] unsafe-buffer-usage-in-format-attr-call --- clang/include/clang/Basic/DiagnosticGroups.td | 4 ++-- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- .../SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 08ad2fc4b954d..2890a0f27e90d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1773,9 +1773,9 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">; // Warnings and fixes to support the "safe buffers" programming model. def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">; def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; -def UnsafeBufferUsageFormatAttr : DiagGroup<"unsafe-buffer-usage-format-attr">; +def UnsafeBufferUsageInFormatAttrCall : DiagGroup<"unsafe-buffer-usage-in-format-attr-call">; def UnsafeBufferUsageInUniquePtrArrayAccess : DiagGroup<"unsafe-buffer-usage-in-unique-ptr-array-access">; -def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageFormatAttr, UnsafeBufferUsageInUniquePtrArrayAccess]>; +def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInFormatAttrCall, UnsafeBufferUsageInUniquePtrArrayAccess]>; // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 967d54bc44979..43315a6f7b7d9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13452,7 +13452,7 @@ def warn_unsafe_buffer_libc_call : Warning< InGroup<UnsafeBufferUsageInLibcCall>, DefaultIgnore; def warn_unsafe_buffer_format_attr_call : Warning< "formatting function %0 is unsafe">, - InGroup<UnsafeBufferUsageFormatAttr>, DefaultIgnore; + InGroup<UnsafeBufferUsageInFormatAttrCall>, DefaultIgnore; def note_unsafe_buffer_printf_call : Note< "%select{|change to 'snprintf' for explicit bounds checking | buffer pointer and size may not match" 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 7d6b118154bec..8a8d3bd77711b 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp @@ -3,9 +3,11 @@ // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -Wno-gcc-compat\ // RUN: -verify %s -x objective-c++ // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \ -// RUN: -Wunsafe-buffer-usage-format-attr -Wno-gcc-compat -verify %s +// RUN: -Wunsafe-buffer-usage-in-format-attr-call -Wno-gcc-compat \ +// RUN: -verify %s // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \ -// RUN: -Wunsafe-buffer-usage-format-attr -Wno-gcc-compat -verify %s -DTEST_STD_NS +// RUN: -Wunsafe-buffer-usage-in-format-attr-call -Wno-gcc-compat \ +// RUN: -verify %s -DTEST_STD_NS typedef struct {} FILE; typedef unsigned int size_t; >From 2d86922f86caabc6ae7594bbae98740d36f31924 Mon Sep 17 00:00:00 2001 From: Hans Wennborg <[email protected]> Date: Mon, 19 Jan 2026 10:26:24 +0100 Subject: [PATCH 3/3] comment about the 0x8 --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index df7488c3059e7..4d163ed281c12 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2525,6 +2525,9 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { const Expr *UnsafeArg = nullptr) override { unsigned DiagID = diag::warn_unsafe_buffer_libc_call; if (PrintfInfo & 0x8) { + // The callee is a function with the format attribute. See the + // documentation of PrintfInfo in UnsafeBufferUsageHandler, and + // UnsafeLibcFunctionCallGadget::UnsafeKind. DiagID = diag::warn_unsafe_buffer_format_attr_call; PrintfInfo ^= 0x8; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
