[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