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 +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;