[clang] [clang] Suppress safe constant pointer arithmetic under -Wno-unsafe-buffer-usage-in-static-sized-array (PR #212322)
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 18 10:46:52 PDT 2026
https://github.com/ymand updated https://github.com/llvm/llvm-project/pull/212322
>From c78e895f8bbdb525d370eb352eaaca09588e69ac Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Mon, 27 Jul 2026 14:22:23 -0400
Subject: [PATCH 1/4] Suppress safe constant pointer arithmetic under
-Wno-unsafe-buffer-usage-in-static-sized-array
If the flag is enabled, pointer arithmetic warnings are suppressed if the offset
is a non-negative constant strictly within the bounds of the array.
---
.../Analyses/UnsafeBufferUsageGadgets.def | 2 +-
clang/lib/Analysis/UnsafeBufferUsage.cpp | 64 +++++++++++++++++--
...fer-usage-in-static-sized-array-unsafe.cpp | 15 +++++
...afe-buffer-usage-in-static-sized-array.cpp | 9 +++
4 files changed, 82 insertions(+), 8 deletions(-)
create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array-unsafe.cpp
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 7ce3c5f0fc7c5..38fd9d316b34c 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -33,7 +33,7 @@
WARNING_GADGET(Increment)
WARNING_GADGET(Decrement)
-WARNING_GADGET(PointerArithmetic)
+WARNING_OPTIONAL_GADGET(PointerArithmetic)
WARNING_GADGET(UnsafeBufferUsageAttr)
WARNING_GADGET(UnsafeBufferUsageCtorAttr)
WARNING_GADGET(DataInvocation)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 64b524de829b5..b4dc9dd5bc6a5 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -843,6 +843,39 @@ static bool isSafeArraySubscript(const ArraySubscriptExpr &Node,
return false;
}
+static bool isSafePointerArithmetic(const Expr *Ptr, const Expr *OffsetExpr,
+ BinaryOperatorKind Opcode,
+ const ASTContext &Ctx) {
+ Expr::EvalResult EVResult;
+
+ if (OffsetExpr->isValueDependent() ||
+ !OffsetExpr->EvaluateAsInt(EVResult, Ctx)) {
+ // Dynamic offsets are not safe.
+ return false;
+ }
+
+ uint64_t limit = 0;
+ const Expr *Base = Ptr->IgnoreParenImpCasts();
+
+ if (const auto *CATy = dyn_cast<ConstantArrayType>(
+ Base->getType()->getUnqualifiedDesugaredType())) {
+ limit = CATy->getLimitedSize();
+ } else if (const auto *SLiteral = dyn_cast<clang::StringLiteral>(Base)) {
+ limit = SLiteral->getLength() + 1;
+ } else {
+ return false;
+ }
+
+ llvm::APSInt OffsetVal = EVResult.Val.getInt();
+ bool IsSub = (Opcode == BO_Sub || Opcode == BO_SubAssign);
+ if (IsSub)
+ OffsetVal = -OffsetVal;
+
+ // If the offset is a constant, and it is within the bounds of the
+ // array, then it is safe.
+ return OffsetVal.isNonNegative() && OffsetVal.getLimitedValue() < limit;
+}
+
// Constant fold a conditional expression 'cond ? A : B' to
// - 'A', if 'cond' has constant true value;
// - 'B', if 'cond' has constant false value.
@@ -1764,30 +1797,47 @@ class PointerArithmeticGadget : public WarningGadget {
}
static bool matches(const Stmt *S, const ASTContext &Ctx,
+ const UnsafeBufferUsageHandler *Handler,
MatchResult &Result) {
const auto *BO = dyn_cast<BinaryOperator>(S);
if (!BO)
return false;
const auto *LHS = BO->getLHS();
const auto *RHS = BO->getRHS();
+
+ const Expr *Ptr = nullptr;
+ const Expr *OffsetExpr = nullptr;
+
// ptr at left
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub ||
BO->getOpcode() == BO_AddAssign || BO->getOpcode() == BO_SubAssign) {
if (hasPointerType(*LHS) && (RHS->getType()->isIntegerType() ||
RHS->getType()->isEnumeralType())) {
- Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*LHS));
- Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
- return true;
+ Ptr = LHS;
+ OffsetExpr = RHS;
}
}
// ptr at right
if (BO->getOpcode() == BO_Add && hasPointerType(*RHS) &&
(LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
- Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*RHS));
- Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
- return true;
+ Ptr = RHS;
+ OffsetExpr = LHS;
}
- return false;
+
+ if (!Ptr || !OffsetExpr)
+ return false;
+
+ // If -Wno-unsafe-buffer-usage-in-static-sized-array is used, suppress
+ // warnings for guaranteed safe pointer arithmetic.
+ if (Handler->ignoreUnsafeBufferInStaticSizedArray(S->getBeginLoc()) &&
+ isSafePointerArithmetic(Ptr, OffsetExpr, BO->getOpcode(), Ctx)) {
+ return false;
+ }
+
+ // Default: warn on all pointer arithmetic
+ Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*Ptr));
+ Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
+ return true;
}
void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array-unsafe.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array-unsafe.cpp
new file mode 100644
index 0000000000000..1165c58699456
--- /dev/null
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array-unsafe.cpp
@@ -0,0 +1,15 @@
+// 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
+
+void unsafe_pointer_arithmetic(int idx) {
+ int buffer[10]; // expected-warning {{'buffer' is an unsafe buffer that does not perform bounds checks}}
+
+ int *u1 = buffer + 10; // expected-note {{used in pointer arithmetic here}}
+ int *u2 = buffer + 15; // expected-note {{used in pointer arithmetic here}}
+
+ int *u3 = buffer - 1; // expected-note {{used in pointer arithmetic here}}
+
+ int *u4 = buffer + idx; // expected-note {{used in pointer arithmetic here}}
+}
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
index c4813198bbd9a..9bc49525efdb9 100644
--- 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
@@ -157,3 +157,12 @@ void array_indexed_const_expr(unsigned idx) {
k = arr[get_const(5)];
k = arr[get_const(4)];
}
+
+void safe_pointer_arithmetic() {
+ int arr[10];
+
+ int *p1 = arr + 0;
+ int *p2 = arr + 5;
+ int *p3 = arr + 9;
+ int *p4 = 5 + arr;
+}
>From f2ab6f233581e6bed1d5a9d25be4e2a88fa5f44f Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Mon, 27 Jul 2026 16:28:14 -0400
Subject: [PATCH 2/4] Add release note for
-Wno-unsafe-buffer-usage-in-static-sized-array pointer arithmetic
---
clang/docs/ReleaseNotes.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 943e75080f12f..16911cf384ea3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -396,6 +396,10 @@ features cannot lower the translation-unit ABI level;
- `-Wunsafe-buffer-usage` now warns about unsafe two-parameter constructors of
`std::string_view` (pointer and size), consistent with the existing warning for `std::span`.
+- `-Wno-unsafe-buffer-usage-in-static-sized-array` now also suppresses warnings
+ for pointer arithmetic on statically-sized arrays when the offset is a
+ non-negative constant within the array bounds.
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
>From 7b30cd12d36fe0145cb40f8d2675b85015a2b1b5 Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Tue, 28 Jul 2026 15:42:14 -0400
Subject: [PATCH 3/4] Simplify subtraction check
---
clang/lib/Analysis/UnsafeBufferUsage.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index b4dc9dd5bc6a5..f11b7d0065892 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -867,8 +867,7 @@ static bool isSafePointerArithmetic(const Expr *Ptr, const Expr *OffsetExpr,
}
llvm::APSInt OffsetVal = EVResult.Val.getInt();
- bool IsSub = (Opcode == BO_Sub || Opcode == BO_SubAssign);
- if (IsSub)
+ if (Opcode == BO_Sub)
OffsetVal = -OffsetVal;
// If the offset is a constant, and it is within the bounds of the
>From 7e673872c6c0cafc06452050d3a943e67e2c48a3 Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Mon, 17 Aug 2026 15:52:39 -0400
Subject: [PATCH 4/4] Add test for casting array false negative with FIXME note
---
clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp
index 00daa28b433eb..8c059e1833034 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp
@@ -86,6 +86,13 @@ void constant_idx_unsafe(unsigned idx) {
buffer[10] = 0; // expected-note{{used in buffer access here}}
}
+// FIXME: This is a false negative. The casted type int[10] requires 40 bytes,
+// but the underlying array only has 10 bytes, so accessing index 9 is out-of-bounds.
+void cast_array_subscript_false_negative() {
+ char a[10];
+ ((int(&)[10])a)[9] = 4;
+}
+
void constant_id_string(unsigned idx) {
char safe_char = "abc"[1]; // no-warning
safe_char = ""[0];
More information about the cfe-commits
mailing list