From 74becf4f36b0ec7cf5028598950f2528ba1903a9 Mon Sep 17 00:00:00 2001 From: Usama Hameed Date: Mon, 27 Apr 2026 14:02:42 -0700 Subject: [PATCH] [clang][BoundsSafety][UBSan] Fix false-positive UBSan pointer overflow check for pointer subtraction When -fbounds-safety and -fsanitize=pointer-overflow are both enabled, pointer subtraction unconditionally triggers a UBSan pointer overflow false positive. EmitCheckedBoundPointerArithmetic was passing the raw positive index to EmitCheckedInBoundsGEP without negating it first, causing the check to verify (base + offset) ule base instead of (base - offset) ule base, which is always false for any positive offset. All other callers of EmitCheckedInBoundsGEP pre-negate the index for subtraction. Fix this by negating the index with CreateNeg before passing it to EmitCheckedInBoundsGEP when IsSub is true. rdar://173944149 --- clang/lib/CodeGen/CGExprAgg.cpp | 4 ++++ .../CodeGen/ubsan/ptr-overflow-subtraction.c | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-subtraction.c diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 562cf12bf576c..1c9b34489cc5d 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1978,6 +1978,10 @@ void AggExprEmitter::EmitCheckedBoundPointerArithmetic( LValue BaseLV = CGF.EmitAggExprToLValue(Base); EmitBoundPointerArithmetic(DestLV, BaseLV, Idx, IsSigned, IsSub); + if (IsSub) { + Idx = Builder.CreateNeg(Idx, "idx.neg"); + } + if (CGF.SanOpts.has(SanitizerKind::ArrayBounds)) CGF.EmitBoundsCheck(E, Base, Idx, IdxType, /*Accessed*/ false); diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-subtraction.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-subtraction.c new file mode 100644 index 0000000000000..491987afed861 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-subtraction.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include + +// Regression test for pointer subtraction generating a false-positive +// UBSan pointer overflow check when -fbounds-safety is enabled. +// The check must use the negated index so that (base - offset) ule base +// is checked, not (base + offset) ule base which is unconditionally false. +void ptr_sub(unsigned char * __bidi_indexable ptr, unsigned int offset) { + unsigned char * __bidi_indexable ptr2 = ptr - offset; + (void)ptr2; +} + +// CHECK: %[[OFFSET:[a-z0-9]+]] = load i32 +// CHECK: %[[NEG:[a-z0-9.]+]] = sub i32 0, %[[OFFSET]] +// CHECK: getelementptr{{.*}} %[[NEG]] +// CHECK: %[[EXT:[a-z0-9.]+]] = sext i32 %[[NEG]] to i64 +// CHECK: call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[EXT]])