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