[clang] e2551c1 - [analyzer] Fix a false memory leak reports involving placement new (#144341)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 17 01:31:41 PDT 2025
Author: Arseniy Zaostrovnykh
Date: 2025-06-17T10:31:38+02:00
New Revision: e2551c14d0d9180ccaef9d33c524d83e7813a361
URL: https://github.com/llvm/llvm-project/commit/e2551c14d0d9180ccaef9d33c524d83e7813a361
DIFF: https://github.com/llvm/llvm-project/commit/e2551c14d0d9180ccaef9d33c524d83e7813a361.diff
LOG: [analyzer] Fix a false memory leak reports involving placement new (#144341)
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.
Note that the there are two syntaxes to invoke placement new:
`new (p) int` and an explicit operator call `operator new(sizeof(int), p)`.
The first syntax was already properly handled by the engine.
This change corrects handling of the second syntax.
CPP-6375
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
clang/test/Analysis/NewDelete-checker-test.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index fef33509c0b6e..35e98a5e2719a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1371,6 +1371,20 @@ void MallocChecker::checkIfFreeNameIndex(ProgramStateRef State,
C.addTransition(State);
}
+const Expr *getPlacementNewBufferArg(const CallExpr *CE,
+ const FunctionDecl *FD) {
+ // 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 || (FD->getOverloadedOperator() != OO_New &&
+ FD->getOverloadedOperator() != OO_Array_New))
+ return nullptr;
+ auto BuffType = FD->getParamDecl(1)->getType();
+ if (BuffType.isNull() || !BuffType->isVoidPointerType())
+ return nullptr;
+ return CE->getArg(1);
+}
+
void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
const CallEvent &Call,
CheckerContext &C) const {
@@ -1386,6 +1400,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..da0eef7c52bd8 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,39 @@ 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 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>
+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;
More information about the cfe-commits
mailing list