https://github.com/pl98 updated https://github.com/llvm/llvm-project/pull/212322
>From f55ff1ef7d65554e731755c710353715d6067d8e Mon Sep 17 00:00:00 2001 From: Phoebe Liang <[email protected]> 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 129ce95c1c0e0..e5f2d3e037d82 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 e80dfc82e60ba..71cb2e573fecf 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -777,6 +777,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. @@ -1698,30 +1731,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 cbe26f3a8359b6784de7a80a9f02c1e4d1510f77 Mon Sep 17 00:00:00 2001 From: Phoebe Liang <[email protected]> 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 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 7b829db478088..d3379267d4f11 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -335,6 +335,9 @@ features cannot lower the translation-unit ABI level; - Clang now attempts to print enumerator names rather than C-style cast expressions in more diagnostics. +- `-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 >From 025e7000778fef6ea86a633c7509880804091cbb Mon Sep 17 00:00:00 2001 From: Phoebe Liang <[email protected]> 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 71cb2e573fecf..f84b74ba37a4d 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -801,8 +801,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 9e2621ab15d3b3bd5216f51e66b25d243e9ff7f6 Mon Sep 17 00:00:00 2001 From: Phoebe Liang <[email protected]> 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]; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
