[llvm-branch-commits] [clang] [-Wunsafe-buffer-usage] Separate flag for format-attributed functions (#175749) (PR #176876)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jan 20 01:11:07 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Hans Wennborg (zmodem)
<details>
<summary>Changes</summary>
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-in-format-attr-call flag (implicitly enabled by -Wunsafe-buffer-usage), allowing projects to decide whether they want to opt in to this or not.
(cherry picked from commit 3d90b7a2d7333ead420b18baece183249404329a)
---
Full diff: https://github.com/llvm/llvm-project/pull/176876.diff
6 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h (+2-1)
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+2-1)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4)
- (modified) clang/lib/Analysis/UnsafeBufferUsage.cpp (+9-3)
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+9-1)
- (modified) clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp (+17-15)
``````````diff
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..b7c5195e22c01 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1774,7 +1774,8 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">;
def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">;
def UnsafeBufferUsageInUniquePtrArrayAccess : DiagGroup<"unsafe-buffer-usage-in-unique-ptr-array-access">;
-def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess]>;
+def UnsafeBufferUsageInFormatAttrCall : DiagGroup<"unsafe-buffer-usage-in-format-attr-call">;
+def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess, UnsafeBufferUsageInFormatAttrCall]>;
// 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..43315a6f7b7d9 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<UnsafeBufferUsageInFormatAttrCall>, 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..4d163ed281c12 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2523,7 +2523,15 @@ 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) {
+ // 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;
+ }
+ 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..8a8d3bd77711b 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions.cpp
@@ -2,9 +2,11 @@
// 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: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
+// 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 -Wno-gcc-compat\
+// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
+// RUN: -Wunsafe-buffer-usage-in-format-attr-call -Wno-gcc-compat \
// RUN: -verify %s -DTEST_STD_NS
typedef struct {} FILE;
@@ -295,7 +297,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 +306,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 +327,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 +359,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 +369,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());
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/176876
More information about the llvm-branch-commits
mailing list