https://github.com/necto updated 
https://github.com/llvm/llvm-project/pull/144341

>From 44fe99cfc6ab394ba94301b94323be87f98c06dd Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto...@gmail.com>
Date: Fri, 13 Jun 2025 17:43:57 +0200
Subject: [PATCH 1/4] [analyzer] Fix a false memory leak report involving
 placement new

Placement new does not allocate memory, so it should not be reported as
a memory leak. A recent MallocChecker refactor changed inlining of
placement-new calls with manual evaluation by MallocChecker.
https://github.com/llvm/llvm-project/commit/339282d49f5310a2837da45c0ccc19da15675554

This change avoids marking the value returned by placement new as
allocated and hence avoids the false leak reports.

---
CPP-6375
---
 .../StaticAnalyzer/Checkers/MallocChecker.cpp | 21 ++++++++++
 .../test/Analysis/NewDelete-checker-test.cpp  | 41 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index fef33509c0b6e..4a78f64797bba 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1371,6 +1371,19 @@ void MallocChecker::checkIfFreeNameIndex(ProgramStateRef 
State,
   C.addTransition(State);
 }
 
+bool isVoidStar(QualType T) {
+  return !T.isNull() && T->isPointerType() && 
T->getPointeeType()->isVoidType();
+}
+
+const Expr* getPlacementNewBufferArg(const CallExpr *CE, const FunctionDecl 
*FD) {
+  if (CE->getNumArgs() == 1)
+    return nullptr;
+  // Second argument of placement new must be void*
+  if (!isVoidStar(FD->getParamDecl(1)->getType()))
+    return nullptr;
+  return CE->getArg(1);
+}
+
 void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
                                            const CallEvent &Call,
                                            CheckerContext &C) const {
@@ -1386,6 +1399,14 @@ void 
MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
   // processed by the checkPostStmt callbacks for CXXNewExpr and
   // CXXDeleteExpr.
   const FunctionDecl *FD = C.getCalleeDecl(CE);
+  if (const auto *BufArg = getPlacementNewBufferArg(CE, FD)) {
+    // Placement new does not allocate memory
+    auto RetVal = State->getSVal(BufArg, Call.getLocationContext());
+    State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+    C.addTransition(State);
+    return;
+  }
+
   switch (FD->getOverloadedOperator()) {
   case OO_New:
     State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp 
b/clang/test/Analysis/NewDelete-checker-test.cpp
index 06754f669b1e6..0820600a63559 100644
--- a/clang/test/Analysis/NewDelete-checker-test.cpp
+++ b/clang/test/Analysis/NewDelete-checker-test.cpp
@@ -26,9 +26,10 @@
 // RUN:   -analyzer-checker=cplusplus.NewDeleteLeaks
 //
 // RUN: %clang_analyze_cc1 -std=c++17 -fblocks -verify %s \
-// RUN:   -verify=expected,leak \
+// RUN:   -verify=expected,leak,inspection \
 // RUN:   -analyzer-checker=core \
-// RUN:   -analyzer-checker=cplusplus.NewDeleteLeaks
+// RUN:   -analyzer-checker=cplusplus.NewDeleteLeaks \
+// RUN:   -analyzer-checker=debug.ExprInspection
 
 #include "Inputs/system-header-simulator-cxx.h"
 
@@ -63,6 +64,42 @@ void testGlobalNoThrowPlacementExprNewBeforeOverload() {
   int *p = new(std::nothrow) int;
 } // leak-warning{{Potential leak of memory pointed to by 'p'}}
 
+//----- Standard pointer placement operators
+void testGlobalPointerPlacementNew() {
+  int i;
+  void *p1 = operator new(0, &i); // no warn
+
+  void *p2 = operator new[](0, &i); // no warn
+
+  int *p3 = new(&i) int; // no warn
+
+  int *p4 = new(&i) int[0]; // no warn
+}
+
+template<typename T>
+void clang_analyzer_dump(T x);
+
+void testPlacementNewBufValue() {
+  int i = 10;
+  int *p = new(&i) int;
+  clang_analyzer_dump(p); // inspection-warning{{&i}}
+  clang_analyzer_dump(*p); // inspection-warning{{10}}
+}
+
+void testPlacementNewBufValueExplicitOp() {
+  int i = 10;
+  int *p = (int*)operator new(sizeof(int), &i);
+  clang_analyzer_dump(p); // inspection-warning{{&i}}
+  clang_analyzer_dump(*p); // inspection-warning{{10}}
+}
+
+void testPlacementArrNewBufValueExplicitArrOp() {
+  int i = 10;
+  int *p = (int*)operator new[](sizeof(int), &i);
+  clang_analyzer_dump(p); // inspection-warning{{&i}}
+  clang_analyzer_dump(*p); // inspection-warning{{10}}
+}
+
 //----- Other cases
 void testNewMemoryIsInHeap() {
   int *p = new int;

>From b18957fbc53bdb68245ea87ce09441f31eb9c809 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto...@gmail.com>
Date: Mon, 16 Jun 2025 14:38:15 +0200
Subject: [PATCH 2/4] [NFC] Fix formatting

---
 clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 4a78f64797bba..c0b08d9a28ffa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1375,7 +1375,8 @@ bool isVoidStar(QualType T) {
   return !T.isNull() && T->isPointerType() && 
T->getPointeeType()->isVoidType();
 }
 
-const Expr* getPlacementNewBufferArg(const CallExpr *CE, const FunctionDecl 
*FD) {
+const Expr *getPlacementNewBufferArg(const CallExpr *CE,
+                                     const FunctionDecl *FD) {
   if (CE->getNumArgs() == 1)
     return nullptr;
   // Second argument of placement new must be void*

>From 2b861b6bf1f01ac2376dffe2bdedf32d823797dd Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto...@gmail.com>
Date: Mon, 16 Jun 2025 14:47:21 +0200
Subject: [PATCH 3/4] Use isVoidPointerType() and state the checked signature

---
 clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index c0b08d9a28ffa..ebd98c27a8001 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1371,16 +1371,15 @@ void 
MallocChecker::checkIfFreeNameIndex(ProgramStateRef State,
   C.addTransition(State);
 }
 
-bool isVoidStar(QualType T) {
-  return !T.isNull() && T->isPointerType() && 
T->getPointeeType()->isVoidType();
-}
-
 const Expr *getPlacementNewBufferArg(const CallExpr *CE,
                                      const FunctionDecl *FD) {
-  if (CE->getNumArgs() == 1)
+  // Checking for signature:
+  // void* operator new  ( std::size_t count, void* ptr );
+  // void* operator new[]( std::size_t count, void* ptr );
+  if (CE->getNumArgs() != 2)
     return nullptr;
-  // Second argument of placement new must be void*
-  if (!isVoidStar(FD->getParamDecl(1)->getType()))
+  auto BuffType = FD->getParamDecl(1)->getType();
+  if (BuffType.isNull() || !BuffType->isVoidPointerType())
     return nullptr;
   return CE->getArg(1);
 }

>From 245d8f562a984e4673bf5069774de1b92587f67c Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <arseniy.zaostrovn...@sonarsource.com>
Date: Mon, 16 Jun 2025 15:27:31 +0200
Subject: [PATCH 4/4] Update clang/test/Analysis/NewDelete-checker-test.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics 
<108414871+balazs-benics-sonarsou...@users.noreply.github.com>
---
 clang/test/Analysis/NewDelete-checker-test.cpp | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp 
b/clang/test/Analysis/NewDelete-checker-test.cpp
index 0820600a63559..da0eef7c52bd8 100644
--- a/clang/test/Analysis/NewDelete-checker-test.cpp
+++ b/clang/test/Analysis/NewDelete-checker-test.cpp
@@ -67,13 +67,10 @@ void testGlobalNoThrowPlacementExprNewBeforeOverload() {
 //----- Standard pointer placement operators
 void testGlobalPointerPlacementNew() {
   int i;
-  void *p1 = operator new(0, &i); // no warn
-
-  void *p2 = operator new[](0, &i); // no warn
-
-  int *p3 = new(&i) int; // no warn
-
-  int *p4 = new(&i) int[0]; // no warn
+  void *p1 = operator new(0, &i); // no leak: placement new never allocates
+  void *p2 = operator new[](0, &i); // no leak
+  int *p3 = new(&i) int; // no leak
+  int *p4 = new(&i) int[0]; // no leak
 }
 
 template<typename T>

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

Reply via email to