[clang] 762a44f - [clang][Wunsafe-buffer-usage] Add -Wunsafe-buffer-usage-in-static-sized-array (#176466)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 16 13:27:41 PST 2026
Author: Max
Date: 2026-01-16T21:27:36Z
New Revision: 762a44f2c3cac98b8d3823936a620ea173cfef96
URL: https://github.com/llvm/llvm-project/commit/762a44f2c3cac98b8d3823936a620ea173cfef96
DIFF: https://github.com/llvm/llvm-project/commit/762a44f2c3cac98b8d3823936a620ea173cfef96.diff
LOG: [clang][Wunsafe-buffer-usage] Add -Wunsafe-buffer-usage-in-static-sized-array (#176466)
This PR adds support for toggling on/off warnings around static sized
arrays. This supports / addresses
https://github.com/llvm/llvm-project/issues/87284, for those who use
-fsanitize=array-bounds which inserts checks for fixed sized arrays
already.
Added:
clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp
Modified:
clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/UnsafeBufferUsage.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index ea41eb3becfcf..462dabcc41da7 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -178,6 +178,11 @@ class UnsafeBufferUsageHandler {
virtual bool
ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const = 0;
+ /// \return true iff array subscript accesses on fixed size arrays should NOT
+ /// be reported at `Loc`
+ virtual bool
+ ignoreUnsafeBufferInStaticSizedArray(const SourceLocation &Loc) const = 0;
+
virtual std::string
getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
StringRef WSSuffix = "") const = 0;
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index f9bba5d54e9c7..129ce95c1c0e0 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -33,12 +33,12 @@
WARNING_GADGET(Increment)
WARNING_GADGET(Decrement)
-WARNING_GADGET(ArraySubscript)
WARNING_GADGET(PointerArithmetic)
WARNING_GADGET(UnsafeBufferUsageAttr)
WARNING_GADGET(UnsafeBufferUsageCtorAttr)
WARNING_GADGET(DataInvocation)
WARNING_GADGET(UniquePtrArrayAccess)
+WARNING_OPTIONAL_GADGET(ArraySubscript)
WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall)
WARNING_OPTIONAL_GADGET(UnsafeFormatAttributedFunctionCall)
WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)`
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 3764475fbd3df..f4717505e1bdd 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 UnsafeBufferUsageInStaticSizedArray : DiagGroup<"unsafe-buffer-usage-in-static-sized-array">;
+def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess, UnsafeBufferUsageInStaticSizedArray]>;
// 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 eb7a608f798b8..1ab3f537d36a3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13472,6 +13472,8 @@ def warn_unsafe_buffer_usage_in_container : Warning<
InGroup<UnsafeBufferUsageInContainer>, DefaultIgnore;
def warn_unsafe_buffer_usage_unique_ptr_array_access : Warning<"direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking">,
InGroup<UnsafeBufferUsageInUniquePtrArrayAccess>, DefaultIgnore;
+def warn_unsafe_buffer_usage_in_static_sized_array : Warning<"direct access on T[N] is unsafe due to the lack of bounds checking">,
+ InGroup<UnsafeBufferUsageInStaticSizedArray>, DefaultIgnore;
#ifndef NDEBUG
// Not a user-facing diagnostic. Useful for debugging false negatives in
// -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 3e4d14c51ca1e..2abc3d7a3963f 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -667,7 +667,8 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node,
}
static bool isSafeArraySubscript(const ArraySubscriptExpr &Node,
- const ASTContext &Ctx) {
+ const ASTContext &Ctx,
+ const bool IgnoreStaticSizedArrays) {
// FIXME: Proper solution:
// - refactor Sema::CheckArrayAccess
// - split safe/OOB/unknown decision logic from diagnostics emitting code
@@ -689,6 +690,12 @@ static bool isSafeArraySubscript(const ArraySubscriptExpr &Node,
return false;
}
+ if (IgnoreStaticSizedArrays) {
+ // If we made it here, it means a size was found for the var being accessed
+ // (either string literal or array). If it's fixed size, we can ignore it.
+ return true;
+ }
+
Expr::EvalResult EVResult;
const Expr *IndexExpr = Node.getIdx();
if (!IndexExpr->isValueDependent() &&
@@ -1568,6 +1575,7 @@ class ArraySubscriptGadget : public WarningGadget {
}
static bool matches(const Stmt *S, const ASTContext &Ctx,
+ const UnsafeBufferUsageHandler *Handler,
MatchResult &Result) {
const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
if (!ASE)
@@ -1578,7 +1586,10 @@ class ArraySubscriptGadget : public WarningGadget {
const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
isa<ArrayInitIndexExpr>(ASE->getIdx());
- if (IsSafeIndex || isSafeArraySubscript(*ASE, Ctx))
+ if (IsSafeIndex ||
+ isSafeArraySubscript(
+ *ASE, Ctx,
+ Handler->ignoreUnsafeBufferInStaticSizedArray(S->getBeginLoc())))
return false;
Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
return true;
@@ -2888,6 +2899,10 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
return false;
}
+ bool ignoreUnsafeBufferInStaticSizedArray(
+ const SourceLocation &Loc) const override {
+ return false;
+ }
std::string getUnsafeBufferUsageAttributeTextAt(
SourceLocation, StringRef WSSuffix = "") const override {
return "";
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 20ee36b46aa13..d297d9e80cf2f 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2630,6 +2630,12 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
return S.Diags.isIgnored(diag::warn_unsafe_buffer_libc_call, Loc);
}
+ bool ignoreUnsafeBufferInStaticSizedArray(
+ const SourceLocation &Loc) const override {
+ return S.Diags.isIgnored(
+ diag::warn_unsafe_buffer_usage_in_static_sized_array, Loc);
+ }
+
// Returns the text representation of clang::unsafe_buffer_usage attribute.
// `WSSuffix` holds customized "white-space"s, e.g., newline or whilespace
// characters.
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp
new file mode 100644
index 0000000000000..c4813198bbd9a
--- /dev/null
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp
@@ -0,0 +1,159 @@
+// RUN: %clang_cc1 -std=c++20 -Wno-everything -Wunsafe-buffer-usage \
+// RUN: -Wno-unsafe-buffer-usage-in-static-sized-array \
+// RUN: -fsafe-buffer-usage-suggestions \
+// RUN: -verify %s
+
+// CHECK-NOT: [-Wunsafe-buffer-usage]
+// expected-no-diagnostics
+
+void foo(unsigned idx) {
+ int buffer[10];
+ buffer[idx] = 0;
+}
+
+int global_buffer[10];
+void foo2(unsigned idx) { global_buffer[idx] = 0; }
+
+struct Foo {
+ int member_buffer[10];
+ int x;
+};
+
+void foo2(Foo &f, unsigned idx) { f.member_buffer[idx] = 0; }
+
+void constant_idx_safe(unsigned idx) {
+ int buffer[10];
+ buffer[9] = 0;
+}
+
+void constant_idx_safe0(unsigned idx) {
+ int buffer[10];
+ buffer[0] = 0;
+}
+
+int array[10];
+
+void circular_access_unsigned(unsigned idx) {
+ array[idx % 10];
+ array[idx % 11];
+ array[(idx + 3) % 10];
+ array[(--idx) % 8];
+ array[idx & 9 % 10];
+ array[9 & idx % 11];
+ array[12 % 10];
+}
+
+void circular_access_signed(int idx) { array[idx % 10]; }
+
+void masked_idx1(unsigned long long idx, Foo f) {
+ // Bitwise and operation
+ array[idx & 5] = 10;
+ array[5 & idx] = 12;
+ array[idx & 11 & 5] = 3;
+ array[idx & 11] = 20;
+ array[idx &= 5];
+ array[f.x & 5];
+ array[5 & f.x];
+ array[f.x & (-5)];
+}
+
+typedef unsigned long long uint64_t;
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+
+void type_conversions(uint64_t idx1, uint32_t idx2, uint8_t idx3) {
+ array[(uint32_t)idx1 & 3];
+ array[idx2 & 3];
+ array[idx3 & 3];
+}
+
+int array2[5];
+
+void masked_idx_safe(unsigned long long idx) {
+ array2[6 & 5];
+ array2[6 & idx & (idx + 1) & 5];
+}
+
+void constant_idx_unsafe(unsigned idx) {
+ int buffer[10];
+ buffer[10] = 0;
+}
+
+void constant_id_string(unsigned idx) {
+ char safe_char = "abc"[1];
+ safe_char = ""[0];
+ safe_char = "\0"[0];
+
+ char abcd[5] = "abc";
+ abcd[2];
+
+ char unsafe_char = "abc"[3];
+ unsafe_char = "abc"[-1];
+ unsafe_char = ""[1];
+ unsafe_char = ""[idx];
+}
+
+typedef float Float4x4[4][4];
+
+float two_dimension_array(Float4x4 &matrix, unsigned idx) {
+ float a = matrix[0][4];
+
+ a = matrix[0][3];
+
+ a = matrix[4][0];
+
+ a = matrix[idx][0];
+
+ a = matrix[0][idx];
+
+ a = matrix[idx][idx];
+
+ return matrix[1][1];
+}
+
+typedef float Float2x3x4[2][3][4];
+float multi_dimension_array(Float2x3x4 &matrix) {
+ float *f = matrix[0][2];
+ return matrix[1][2][3];
+}
+
+char array_strings[][11] = {"Apple", "Banana", "Cherry", "Date", "Elderberry"};
+
+char array_string[] = "123456";
+
+char access_strings() {
+ char c = array_strings[0][4];
+ c = array_strings[3][10];
+ c = array_string[5];
+ return c;
+}
+
+struct T {
+ int array[10];
+};
+
+const int index = 1;
+
+constexpr int get_const(int x) {
+ if (x < 3)
+ return ++x;
+ else
+ return x + 5;
+};
+
+void array_indexed_const_expr(unsigned idx) {
+ int arr[10];
+ arr[sizeof(int)] = 5;
+
+ int array[sizeof(T)];
+ array[sizeof(int)] = 5;
+ array[sizeof(T) - 1] = 3;
+
+ int k = arr[6 & 5];
+ k = arr[2 << index];
+ k = arr[8 << index];
+ k = arr[16 >> 1];
+ k = arr[get_const(index)];
+ k = arr[get_const(5)];
+ k = arr[get_const(4)];
+}
More information about the cfe-commits
mailing list