[analyzer] For now assume all standard global 'operator new' functions allocate memory in heap.

+ Improved test coverage for cplusplus.NewDelete checker.

llvm-svn: 178244
This commit is contained in:
Anton Yartsev 2013-03-28 16:10:38 +00:00
parent c2aa348dd0
commit 8b662704dc
5 changed files with 161 additions and 59 deletions

View File

@ -457,6 +457,10 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const
return false; return false;
} }
// Tells if the callee is one of the following:
// 1) A global non-placement new/delete operator function.
// 2) A global placement operator function with the single placement argument
// of type std::nothrow_t.
bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
ASTContext &C) const { ASTContext &C) const {
if (!FD) if (!FD)
@ -467,9 +471,8 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
Kind != OO_Delete && Kind != OO_Array_Delete) Kind != OO_Delete && Kind != OO_Array_Delete)
return false; return false;
// Skip custom new operators. // Skip all operator new/delete methods.
if (!FD->isImplicit() && if (isa<CXXMethodDecl>(FD))
!C.getSourceManager().isInSystemHeader(FD->getLocStart()))
return false; return false;
// Return true if tested operator is a standard placement nothrow operator. // Return true if tested operator is a standard placement nothrow operator.

View File

@ -278,11 +278,32 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
unsigned blockCount = currBldrCtx->blockCount(); unsigned blockCount = currBldrCtx->blockCount();
const LocationContext *LCtx = Pred->getLocationContext(); const LocationContext *LCtx = Pred->getLocationContext();
DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, DefinedOrUnknownSVal symVal = UnknownVal();
CNE->getType(), FunctionDecl *FD = CNE->getOperatorNew();
blockCount);
ProgramStateRef State = Pred->getState();
bool IsStandardGlobalOpNewFunction = false;
if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) {
if (FD->getNumParams() == 2) {
QualType T = FD->getParamDecl(1)->getType();
if (const IdentifierInfo *II = T.getBaseTypeIdentifier())
// NoThrow placement new behaves as a standard new.
IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t");
}
else
// Placement forms are considered non-standard.
IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1);
}
// We assume all standard global 'operator new' functions allocate memory in
// heap. We realize this is an approximation that might not correctly model
// a custom global allocator.
if (IsStandardGlobalOpNewFunction)
symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
else
symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, CNE->getType(),
blockCount);
ProgramStateRef State = Pred->getState();
CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call = CallEventRef<CXXAllocatorCall> Call =
CEMgr.getCXXAllocatorCall(CNE, State, LCtx); CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
@ -296,7 +317,6 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// is not declared as non-throwing, failures /must/ be signalled by // is not declared as non-throwing, failures /must/ be signalled by
// exceptions, and thus the return value will never be NULL. // exceptions, and thus the return value will never be NULL.
// C++11 [basic.stc.dynamic.allocation]p3. // C++11 [basic.stc.dynamic.allocation]p3.
FunctionDecl *FD = CNE->getOperatorNew();
if (FD && getContext().getLangOpts().CXXExceptions) { if (FD && getContext().getLangOpts().CXXExceptions) {
QualType Ty = FD->getType(); QualType Ty = FD->getType();
if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>())

View File

@ -11,68 +11,63 @@ int *global;
// check for leaks // check for leaks
//------------------ //------------------
void testGlobalExprNewBeforeOverload1() { //----- Standard non-placement operators
int *p = new int; void testGlobalOpNew() {
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalExprNewBeforeOverload2() {
int *p = ::new int;
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalOpNewBeforeOverload() {
void *p = operator new(0); void *p = operator new(0);
} // expected-warning{{Memory is never released; potential leak}} } // expected-warning{{Memory is never released; potential leak}}
void testMemIsOnHeap() { void testGlobalOpNewArray() {
void *p = operator new[](0);
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalNewExpr() {
int *p = new int; int *p = new int;
if (global != p)
global = p;
} // expected-warning{{Memory is never released; potential leak}} } // expected-warning{{Memory is never released; potential leak}}
//FIXME: currently a memory region for 'new' is not a heap region, that lead to
//false-positive 'memory leak' ('global != p' is not evaluated to true and 'p'
//does not escape)
void *operator new(std::size_t); void testGlobalNewExprArray() {
void *operator new(std::size_t, double d); int *p = new int[0];
void *operator new[](std::size_t); } // expected-warning{{Memory is never released; potential leak}}
void *operator new[](std::size_t, double d);
void testExprPlacementNew() { //----- Standard nothrow placement operators
void testGlobalNoThrowPlacementOpNewBeforeOverload() {
void *p = operator new(0, std::nothrow);
} // expected-warning{{Memory is never released; potential leak}}
void testGlobalNoThrowPlacementExprNewBeforeOverload() {
int *p = new(std::nothrow) int;
} // expected-warning{{Memory is never released; potential leak}}
//----- Standard pointer placement operators
void testGlobalPointerPlacementNew() {
int i; int i;
int *p1 = new(&i) int; // no warn - standard placement new
int *p2 = new(1.0) int; // no warn - overloaded placement new void *p1 = operator new(0, &i); // no warn
int *p3 = new (std::nothrow) int; void *p2 = operator new[](0, &i); // no warn
} // expected-warning{{Memory is never released; potential leak}}
void testExprPlacementNewArray() { int *p3 = new(&i) int; // no warn
int i;
int *p1 = new(&i) int[1]; // no warn - standard placement new[]
int *p2 = new(1.0) int[1]; // no warn - overloaded placement new[] int *p4 = new(&i) int[0]; // no warn
int *p3 = new (std::nothrow) int[1];
} // expected-warning{{Memory is never released; potential leak}}
void testCustomOpNew() {
void *p = operator new(0); // no warn - call to a custom new
} }
void testGlobalExprNew() { //----- Other cases
void *p = ::new int; // no warn - call to a custom new void testNewMemoryIsInHeap() {
int *p = new int;
if (global != p) // condition is always true as 'p' wraps a heap region that
// is different from a region wrapped by 'global'
global = p; // pointer escapes
} }
void testCustomExprNew() { struct PtrWrapper {
int *p = new int; // no warn - call to a custom new int *x;
}
void testGlobalExprNewArray() { PtrWrapper(int *input) : x(input) {}
void *p = ::new int[1]; // no warn - call to a custom new };
}
void testOverloadedExprNewArray() { void testNewInvalidationPlacement(PtrWrapper *w) {
int *p = new int[1]; // no warn - call to a custom new // Ensure that we don't consider this a leak.
new (w) PtrWrapper(new int); // no warn
} }
//--------------- //---------------
@ -121,22 +116,30 @@ void testAllocDeallocNames() {
// malloc()/free() are subjects of unix.Malloc and unix.MallocWithAnnotations // malloc()/free() are subjects of unix.Malloc and unix.MallocWithAnnotations
void testMallocFreeNoWarn() { void testMallocFreeNoWarn() {
int i; int i;
free(&i); // no-warning free(&i); // no warn
int *p1 = (int *)malloc(sizeof(int)); int *p1 = (int *)malloc(sizeof(int));
free(++p1); // no-warning free(++p1); // no warn
int *p2 = (int *)malloc(sizeof(int)); int *p2 = (int *)malloc(sizeof(int));
free(p2); free(p2);
free(p2); // no-warning free(p2); // no warn
int *p3 = (int *)malloc(sizeof(int)); // no-warning int *p3 = (int *)malloc(sizeof(int)); // no warn
} }
void testFreeNewed() { //----- Test free standard new
void testFreeOpNew() {
void *p = operator new(0);
free(p);
} // expected-warning{{Memory is never released; potential leak}}
// FIXME: Pointer should escape
void testFreeNewExpr() {
int *p = new int; int *p = new int;
free(p); // pointer escaped, no-warning free(p);
} } // expected-warning{{Memory is never released; potential leak}}
// FIXME: Pointer should escape
void testObjcFreeNewed() { void testObjcFreeNewed() {
int *p = new int; int *p = new int;

View File

@ -0,0 +1,57 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s
#include "Inputs/system-header-simulator-cxx.h"
void *allocator(std::size_t size);
void *operator new[](std::size_t size) throw() { return allocator(size); }
void *operator new(std::size_t size) throw() { return allocator(size); }
void *operator new(std::size_t size, std::nothrow_t& nothrow) throw() { return allocator(size); }
void *operator new(std::size_t, double d);
class C {
public:
void *operator new(std::size_t);
};
void testNewMethod() {
void *p1 = C::operator new(0); // no warn
C *p2 = new C; // no warn
C *c3 = ::new C;
} // expected-warning{{Memory is never released; potential leak}}
void testOpNewArray() {
void *p = operator new[](0);
} //FIXME: expected 'Memory is never released; potential leak'
void testNewExprArray() {
int *p = new int[0];
} // expected-warning{{Memory is never released; potential leak}}
//----- Custom non-placement operators
void testOpNew() {
void *p = operator new(0);
} //FIXME: expected 'Memory is never released; potential leak'
void testNewExpr() {
int *p = new int;
} // expected-warning{{Memory is never released; potential leak}}
//----- Custom NoThrow placement operators
void testOpNewNoThrow() {
void *p = operator new(0, std::nothrow);
} // expected-warning{{Memory is never released; potential leak}}
void testNewExprNoThrow() {
int *p = new(std::nothrow) int;
} // expected-warning{{Memory is never released; potential leak}}
//----- Custom placement operators
void testOpNewPlacement() {
void *p = operator new(0, 0.1); // no warn
}
void testNewExprPlacement() {
int *p = new(0.1) int; // no warn
}

View File

@ -0,0 +1,19 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s
// expected-no-diagnostics
namespace std {
typedef __typeof__(sizeof(int)) size_t;
}
void *operator new(std::size_t, ...);
void *operator new[](std::size_t, ...);
void testGlobalCustomVariadicNew() {
void *p1 = operator new(0); // no warn
void *p2 = operator new[](0); // no warn
int *p3 = new int; // no warn
int *p4 = new int[0]; // no warn
}