Author: Phoebe Liang
Date: 2026-08-18T14:49:50-04:00
New Revision: d6a265a477d2feba1d58f97c26ad14683fb8d1ca

URL: 
https://github.com/llvm/llvm-project/commit/d6a265a477d2feba1d58f97c26ad14683fb8d1ca
DIFF: 
https://github.com/llvm/llvm-project/commit/d6a265a477d2feba1d58f97c26ad14683fb8d1ca.diff

LOG: [clang] Suppress safe constant pointer arithmetic under 
-Wno-unsafe-buffer-usage-in-static-sized-array (#212322)

Previously, the opt-out flag only suppressed subscript access warnings
on static arrays, but did not suppress pointer arithmetic warnings,
leaving many false positives.

With this change:
If the flag is enabled, pointer arithmetic is suppressed ONLY if the
compiler can statically prove that the offset is a non-negative constant
strictly within the bounds of the array (offset < size). All dynamic
pointer arithmetic, one-past-the-end calculations, and out-of-bounds
constants continue to emit warnings.

Partially addresses #87284.

_Disclosure: An AI coding assistant (Google Gemini) was used to draft
the initial implementation but the final logic in this PR was rewritten
by hand._

Added: 
    clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array-unsafe.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
    clang/lib/Analysis/UnsafeBufferUsage.cpp
    clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp
    clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp

Removed: 
    


################################################################################
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

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..f11b7d0065892 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -843,6 +843,38 @@ 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();
+  if (Opcode == BO_Sub)
+    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 +1796,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-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];

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;
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to