Skip to content

Commit e2551c1

Browse files
authored
[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. 339282d 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
1 parent 85b110e commit e2551c1

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,20 @@ void MallocChecker::checkIfFreeNameIndex(ProgramStateRef State,
13711371
C.addTransition(State);
13721372
}
13731373

1374+
const Expr *getPlacementNewBufferArg(const CallExpr *CE,
1375+
const FunctionDecl *FD) {
1376+
// Checking for signature:
1377+
// void* operator new ( std::size_t count, void* ptr );
1378+
// void* operator new[]( std::size_t count, void* ptr );
1379+
if (CE->getNumArgs() != 2 || (FD->getOverloadedOperator() != OO_New &&
1380+
FD->getOverloadedOperator() != OO_Array_New))
1381+
return nullptr;
1382+
auto BuffType = FD->getParamDecl(1)->getType();
1383+
if (BuffType.isNull() || !BuffType->isVoidPointerType())
1384+
return nullptr;
1385+
return CE->getArg(1);
1386+
}
1387+
13741388
void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
13751389
const CallEvent &Call,
13761390
CheckerContext &C) const {
@@ -1386,6 +1400,14 @@ void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
13861400
// processed by the checkPostStmt callbacks for CXXNewExpr and
13871401
// CXXDeleteExpr.
13881402
const FunctionDecl *FD = C.getCalleeDecl(CE);
1403+
if (const auto *BufArg = getPlacementNewBufferArg(CE, FD)) {
1404+
// Placement new does not allocate memory
1405+
auto RetVal = State->getSVal(BufArg, Call.getLocationContext());
1406+
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
1407+
C.addTransition(State);
1408+
return;
1409+
}
1410+
13891411
switch (FD->getOverloadedOperator()) {
13901412
case OO_New:
13911413
State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,

clang/test/Analysis/NewDelete-checker-test.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks
2727
//
2828
// RUN: %clang_analyze_cc1 -std=c++17 -fblocks -verify %s \
29-
// RUN: -verify=expected,leak \
29+
// RUN: -verify=expected,leak,inspection \
3030
// RUN: -analyzer-checker=core \
31-
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks
31+
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \
32+
// RUN: -analyzer-checker=debug.ExprInspection
3233

3334
#include "Inputs/system-header-simulator-cxx.h"
3435

@@ -63,6 +64,39 @@ void testGlobalNoThrowPlacementExprNewBeforeOverload() {
6364
int *p = new(std::nothrow) int;
6465
} // leak-warning{{Potential leak of memory pointed to by 'p'}}
6566

67+
//----- Standard pointer placement operators
68+
void testGlobalPointerPlacementNew() {
69+
int i;
70+
void *p1 = operator new(0, &i); // no leak: placement new never allocates
71+
void *p2 = operator new[](0, &i); // no leak
72+
int *p3 = new(&i) int; // no leak
73+
int *p4 = new(&i) int[0]; // no leak
74+
}
75+
76+
template<typename T>
77+
void clang_analyzer_dump(T x);
78+
79+
void testPlacementNewBufValue() {
80+
int i = 10;
81+
int *p = new(&i) int;
82+
clang_analyzer_dump(p); // inspection-warning{{&i}}
83+
clang_analyzer_dump(*p); // inspection-warning{{10}}
84+
}
85+
86+
void testPlacementNewBufValueExplicitOp() {
87+
int i = 10;
88+
int *p = (int*)operator new(sizeof(int), &i);
89+
clang_analyzer_dump(p); // inspection-warning{{&i}}
90+
clang_analyzer_dump(*p); // inspection-warning{{10}}
91+
}
92+
93+
void testPlacementArrNewBufValueExplicitArrOp() {
94+
int i = 10;
95+
int *p = (int*)operator new[](sizeof(int), &i);
96+
clang_analyzer_dump(p); // inspection-warning{{&i}}
97+
clang_analyzer_dump(*p); // inspection-warning{{10}}
98+
}
99+
66100
//----- Other cases
67101
void testNewMemoryIsInHeap() {
68102
int *p = new int;

0 commit comments

Comments
 (0)