https://github.com/alejandro-alvarez-sonarsource updated 
https://github.com/llvm/llvm-project/pull/155855

From a206f1e24596554f2d7221b795c1196e01d74a5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20=C3=81lvarez=20Ayll=C3=B3n?=
 <alejandro.alva...@sonarsource.com>
Date: Mon, 25 Aug 2025 17:02:59 +0200
Subject: [PATCH 1/4] CPP-6868 Fix FP in `PointerArith` for arrays created with
 placement-new

Flag the region as if it was `reinterpret_cast`-ed
---
 .../Checkers/PointerArithChecker.cpp          |  32 ++++-
 clang/test/Analysis/ptr-arith.cpp             | 121 ++++++++++++++++++
 2 files changed, 152 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index 30e01e73eb18f..c4a968950e8bc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -74,6 +74,26 @@ class PointerArithChecker
 
 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
 
+namespace {
+
+bool isArrayPlacementNew(const CXXNewExpr *NE) {
+  return NE->isArray() && NE->getNumPlacementArgs() > 0;
+}
+
+ProgramStateRef markSuperRegionReinterpreted(ProgramStateRef State,
+                                             const MemRegion *Region) {
+  while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
+    Region = BaseRegion->getSuperRegion();
+  }
+  if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
+    State = State->set<RegionState>(ElemRegion->getSuperRegion(),
+                                    AllocKind::Reinterpreted);
+  }
+  return State;
+}
+
+} // namespace
+
 void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
                                            CheckerContext &C) const {
   // TODO: intentional leak. Some information is garbage collected too early,
@@ -244,13 +264,23 @@ void PointerArithChecker::checkPostStmt(const CXXNewExpr 
*NE,
   const MemRegion *Region = AllocedVal.getAsRegion();
   if (!Region)
     return;
+
+  // For array placement-new, mark the original region as reinterpreted
+  if (isArrayPlacementNew(NE)) {
+    State = markSuperRegionReinterpreted(State, Region);
+  }
+
   State = State->set<RegionState>(Region, Kind);
   C.addTransition(State);
 }
 
 void PointerArithChecker::checkPostStmt(const CastExpr *CE,
                                         CheckerContext &C) const {
-  if (CE->getCastKind() != CastKind::CK_BitCast)
+  // Casts to `void*` happen, for instance, on placement new calls.
+  // We consider `void*` not to erase the type information about the underlying
+  // region.
+  if (CE->getCastKind() != CastKind::CK_BitCast ||
+      CE->getType()->isVoidPointerType())
     return;
 
   const Expr *CastedExpr = CE->getSubExpr();
diff --git a/clang/test/Analysis/ptr-arith.cpp 
b/clang/test/Analysis/ptr-arith.cpp
index ec1c75c0c4063..39bb239f51ca7 100644
--- a/clang/test/Analysis/ptr-arith.cpp
+++ b/clang/test/Analysis/ptr-arith.cpp
@@ -165,3 +165,124 @@ void LValueToRValueBitCast_dumps(void *p, char 
(*array)[8]) {
 unsigned long ptr_arithmetic(void *p) {
   return __builtin_bit_cast(unsigned long, p) + 1; // no-crash
 }
+
+
+void escape(int*);
+
+struct AllocOpaqueFlag {};
+
+void* operator new(unsigned long, void *ptr) noexcept { return ptr; }
+void* operator new(unsigned long, void *ptr, AllocOpaqueFlag const&) noexcept 
{ return ptr; }
+
+void* operator new[](unsigned long, void* ptr) noexcept { return ptr; }
+void* operator new[](unsigned long, void* ptr, AllocOpaqueFlag const&);
+
+struct Buffer {
+  char buf[100];
+  int padding;
+};
+
+void checkPlacementNewArryInObject() {
+  Buffer buffer;
+  int* array = new (&buffer) int[10];
+  escape(array);
+  ++array; // no warning
+  (void)*array;
+}
+
+void checkPlacementNewArrayInObjectOpaque() {
+  Buffer buffer;
+  int* array = new (&buffer, AllocOpaqueFlag{}) int[10];
+  escape(array);
+  ++array; // no warning
+  (void)*array;
+}
+
+void checkPlacementNewArrayInArray() {
+  char buffer[100];
+  int* array = new (buffer) int[10];
+  escape(array);
+  ++array; // no warning
+  (void)*array;
+}
+
+void checkPlacementNewArrayInArrayOpaque() {
+  char buffer[100];
+  int* array = new (buffer, AllocOpaqueFlag{}) int;
+  escape(array);
+  ++array; // no warning
+  (void)*array;
+}
+
+void checkPlacementNewObjectInObject() {
+  Buffer buffer;
+  int* array = new (&buffer) int;
+  escape(array);
+  ++array; // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
+  (void)*array;
+}
+
+void checkPlacementNewObjectInObjectOpaque() {
+  Buffer buffer;
+  int* array = new (&buffer, AllocOpaqueFlag{}) int;
+  escape(array);
+  ++array;  // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
+  (void)*array;
+}
+
+void checkPlacementNewObjectInArray() {
+  char buffer[sizeof(int)];
+  int* array = new (buffer) int;
+  escape(array);
+  ++array; // no warning (FN)
+  (void)*array;
+}
+
+void checkPlacementNewObjectInArrayOpaque() {
+  char buffer[sizeof(int)];
+  int* array = new (buffer, AllocOpaqueFlag{}) int;
+  escape(array);
+  ++array; // no warning (FN)
+  (void)*array;
+}
+
+void checkPlacementNewSlices() {
+  const int N = 10;
+  char buffer[sizeof(int) * N] = {0};
+  int *start = new (buffer) int{0};
+  for (int i = 1; i < N; i++) {
+    auto *ptr = new int(buffer[i * sizeof(int)]);
+    *ptr = i;
+  }
+  ++start; // no warning
+  (void*)start;
+}
+
+class BumpAlloc {
+  char *buffer;
+  char *offset;
+public:
+  BumpAlloc(int n): buffer(new char[n]), offset{buffer} {}
+  ~BumpAlloc() {delete [] buffer;}
+
+  void* alloc(unsigned long size) {
+    auto* ptr = offset;
+    offset += size;
+    return ptr;
+  }
+};
+
+void* operator new(unsigned long size, BumpAlloc &ba) {
+  return ba.alloc(size);
+}
+
+void checkPlacementSlab() {
+  BumpAlloc bump{10};
+
+  int *ptr = new (bump) int{0};
+  ++ptr; // no warning
+  (void)*ptr;
+
+  BumpAlloc *why = &bump;
+  ++why; // expected-warning {{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous [alpha.core.PointerArithm]}}
+}

From f81b5e9bf9694b3a81166011f8731168fd6e2532 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20=C3=81lvarez=20Ayll=C3=B3n?=
 <alejandro.alva...@sonarsource.com>
Date: Thu, 28 Aug 2025 16:50:40 +0200
Subject: [PATCH 2/4] Add new positive (which I believe is a TP)

---
 clang/test/Analysis/PR24184.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/PR24184.cpp b/clang/test/Analysis/PR24184.cpp
index 172d3e7e33864..c73271b87de98 100644
--- a/clang/test/Analysis/PR24184.cpp
+++ b/clang/test/Analysis/PR24184.cpp
@@ -56,7 +56,7 @@ void fn1_1(void *p1, const void *p2) { p1 != p2; }
 void fn2_1(uint32_t *p1, unsigned char *p2, uint32_t p3) {
   unsigned i = 0;
   for (0; i < p3; i++)
-    fn1_1(p1 + i, p2 + i * 0);
+    fn1_1(p1 + i, p2 + i * 0); // expected-warning {{Pointer arithmetic on 
non-array variables relies on memory layout, which is dangerous}}
 }
 
 struct A_1 {

From 4b13c0af7d9addc130ab32963f3f575062f9e948 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20=C3=81lvarez=20Ayll=C3=B3n?=
 <alejandro.alva...@sonarsource.com>
Date: Fri, 29 Aug 2025 08:14:41 +0200
Subject: [PATCH 3/4] Feedback

---
 .../Checkers/PointerArithChecker.cpp          | 10 +---
 clang/test/Analysis/ptr-arith.cpp             | 57 ++++++++-----------
 2 files changed, 28 insertions(+), 39 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index c4a968950e8bc..f2fc9219912ad 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -74,14 +74,12 @@ class PointerArithChecker
 
 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
 
-namespace {
-
-bool isArrayPlacementNew(const CXXNewExpr *NE) {
+static bool isArrayPlacementNew(const CXXNewExpr *NE) {
   return NE->isArray() && NE->getNumPlacementArgs() > 0;
 }
 
-ProgramStateRef markSuperRegionReinterpreted(ProgramStateRef State,
-                                             const MemRegion *Region) {
+static ProgramStateRef markSuperRegionReinterpreted(ProgramStateRef State,
+                                                    const MemRegion *Region) {
   while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
     Region = BaseRegion->getSuperRegion();
   }
@@ -92,8 +90,6 @@ ProgramStateRef markSuperRegionReinterpreted(ProgramStateRef 
State,
   return State;
 }
 
-} // namespace
-
 void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
                                            CheckerContext &C) const {
   // TODO: intentional leak. Some information is garbage collected too early,
diff --git a/clang/test/Analysis/ptr-arith.cpp 
b/clang/test/Analysis/ptr-arith.cpp
index 39bb239f51ca7..9f1bc6b2f24d0 100644
--- a/clang/test/Analysis/ptr-arith.cpp
+++ b/clang/test/Analysis/ptr-arith.cpp
@@ -166,16 +166,18 @@ unsigned long ptr_arithmetic(void *p) {
   return __builtin_bit_cast(unsigned long, p) + 1; // no-crash
 }
 
-
-void escape(int*);
-
 struct AllocOpaqueFlag {};
 
-void* operator new(unsigned long, void *ptr) noexcept { return ptr; }
-void* operator new(unsigned long, void *ptr, AllocOpaqueFlag const&) noexcept 
{ return ptr; }
+void *operator new(unsigned long, void *ptr) noexcept { return ptr; }
+void *operator new(unsigned long, void *ptr, AllocOpaqueFlag const &) noexcept 
{
+  return ptr;
+}
 
-void* operator new[](unsigned long, void* ptr) noexcept { return ptr; }
-void* operator new[](unsigned long, void* ptr, AllocOpaqueFlag const&);
+void *operator new[](unsigned long, void *ptr) noexcept { return ptr; }
+void *operator new[](unsigned long, void *ptr,
+                     AllocOpaqueFlag const &) noexcept {
+  return ptr;
+}
 
 struct Buffer {
   char buf[100];
@@ -184,64 +186,56 @@ struct Buffer {
 
 void checkPlacementNewArryInObject() {
   Buffer buffer;
-  int* array = new (&buffer) int[10];
-  escape(array);
+  int *array = new (&buffer) int[10];
   ++array; // no warning
   (void)*array;
 }
 
 void checkPlacementNewArrayInObjectOpaque() {
   Buffer buffer;
-  int* array = new (&buffer, AllocOpaqueFlag{}) int[10];
-  escape(array);
+  int *array = new (&buffer, AllocOpaqueFlag{}) int[10];
   ++array; // no warning
   (void)*array;
 }
 
 void checkPlacementNewArrayInArray() {
   char buffer[100];
-  int* array = new (buffer) int[10];
-  escape(array);
+  int *array = new (buffer) int[10];
   ++array; // no warning
   (void)*array;
 }
 
 void checkPlacementNewArrayInArrayOpaque() {
   char buffer[100];
-  int* array = new (buffer, AllocOpaqueFlag{}) int;
-  escape(array);
+  int *array = new (buffer, AllocOpaqueFlag{}) int;
   ++array; // no warning
   (void)*array;
 }
 
 void checkPlacementNewObjectInObject() {
   Buffer buffer;
-  int* array = new (&buffer) int;
-  escape(array);
+  int *array = new (&buffer) int;
   ++array; // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
   (void)*array;
 }
 
 void checkPlacementNewObjectInObjectOpaque() {
   Buffer buffer;
-  int* array = new (&buffer, AllocOpaqueFlag{}) int;
-  escape(array);
-  ++array;  // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
+  int *array = new (&buffer, AllocOpaqueFlag{}) int;
+  ++array; // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
   (void)*array;
 }
 
 void checkPlacementNewObjectInArray() {
   char buffer[sizeof(int)];
-  int* array = new (buffer) int;
-  escape(array);
+  int *array = new (buffer) int;
   ++array; // no warning (FN)
   (void)*array;
 }
 
 void checkPlacementNewObjectInArrayOpaque() {
   char buffer[sizeof(int)];
-  int* array = new (buffer, AllocOpaqueFlag{}) int;
-  escape(array);
+  int *array = new (buffer, AllocOpaqueFlag{}) int;
   ++array; // no warning (FN)
   (void)*array;
 }
@@ -255,26 +249,25 @@ void checkPlacementNewSlices() {
     *ptr = i;
   }
   ++start; // no warning
-  (void*)start;
+  (void *)start;
 }
 
 class BumpAlloc {
   char *buffer;
   char *offset;
+
 public:
-  BumpAlloc(int n): buffer(new char[n]), offset{buffer} {}
-  ~BumpAlloc() {delete [] buffer;}
+  BumpAlloc(int n) : buffer(new char[n]), offset{buffer} {}
+  ~BumpAlloc() { delete[] buffer; }
 
-  void* alloc(unsigned long size) {
-    auto* ptr = offset;
+  void *alloc(unsigned long size) {
+    auto *ptr = offset;
     offset += size;
     return ptr;
   }
 };
 
-void* operator new(unsigned long size, BumpAlloc &ba) {
-  return ba.alloc(size);
-}
+void *operator new(unsigned long size, BumpAlloc &ba) { return ba.alloc(size); 
}
 
 void checkPlacementSlab() {
   BumpAlloc bump{10};

From b548e8132bf5d38f34a6edd6695063a6daf26da8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20=C3=81lvarez=20Ayll=C3=B3n?=
 <alejandro.alva...@sonarsource.com>
Date: Fri, 29 Aug 2025 08:17:51 +0200
Subject: [PATCH 4/4] Opaque allocator

---
 clang/test/Analysis/ptr-arith.cpp | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/clang/test/Analysis/ptr-arith.cpp 
b/clang/test/Analysis/ptr-arith.cpp
index 9f1bc6b2f24d0..dec1ed19b5dbd 100644
--- a/clang/test/Analysis/ptr-arith.cpp
+++ b/clang/test/Analysis/ptr-arith.cpp
@@ -169,15 +169,11 @@ unsigned long ptr_arithmetic(void *p) {
 struct AllocOpaqueFlag {};
 
 void *operator new(unsigned long, void *ptr) noexcept { return ptr; }
-void *operator new(unsigned long, void *ptr, AllocOpaqueFlag const &) noexcept 
{
-  return ptr;
-}
+void *operator new(unsigned long, void *ptr, AllocOpaqueFlag const &) noexcept;
 
 void *operator new[](unsigned long, void *ptr) noexcept { return ptr; }
 void *operator new[](unsigned long, void *ptr,
-                     AllocOpaqueFlag const &) noexcept {
-  return ptr;
-}
+                     AllocOpaqueFlag const &) noexcept;
 
 struct Buffer {
   char buf[100];
@@ -222,7 +218,7 @@ void checkPlacementNewObjectInObject() {
 void checkPlacementNewObjectInObjectOpaque() {
   Buffer buffer;
   int *array = new (&buffer, AllocOpaqueFlag{}) int;
-  ++array; // expected-warning{{Pointer arithmetic on non-array variables 
relies on memory layout, which is dangerous}}
+  ++array; // no warning (allocator is opaque)
   (void)*array;
 }
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to