[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:
parent
c2aa348dd0
commit
8b662704dc
|
@ -457,6 +457,10 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const
|
|||
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,
|
||||
ASTContext &C) const {
|
||||
if (!FD)
|
||||
|
@ -467,9 +471,8 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
|
|||
Kind != OO_Delete && Kind != OO_Array_Delete)
|
||||
return false;
|
||||
|
||||
// Skip custom new operators.
|
||||
if (!FD->isImplicit() &&
|
||||
!C.getSourceManager().isInSystemHeader(FD->getLocStart()))
|
||||
// Skip all operator new/delete methods.
|
||||
if (isa<CXXMethodDecl>(FD))
|
||||
return false;
|
||||
|
||||
// Return true if tested operator is a standard placement nothrow operator.
|
||||
|
|
|
@ -278,11 +278,32 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
|
|||
|
||||
unsigned blockCount = currBldrCtx->blockCount();
|
||||
const LocationContext *LCtx = Pred->getLocationContext();
|
||||
DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx,
|
||||
CNE->getType(),
|
||||
blockCount);
|
||||
ProgramStateRef State = Pred->getState();
|
||||
DefinedOrUnknownSVal symVal = UnknownVal();
|
||||
FunctionDecl *FD = CNE->getOperatorNew();
|
||||
|
||||
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();
|
||||
CallEventRef<CXXAllocatorCall> Call =
|
||||
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
|
||||
// exceptions, and thus the return value will never be NULL.
|
||||
// C++11 [basic.stc.dynamic.allocation]p3.
|
||||
FunctionDecl *FD = CNE->getOperatorNew();
|
||||
if (FD && getContext().getLangOpts().CXXExceptions) {
|
||||
QualType Ty = FD->getType();
|
||||
if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>())
|
||||
|
|
|
@ -11,68 +11,63 @@ int *global;
|
|||
// check for leaks
|
||||
//------------------
|
||||
|
||||
void testGlobalExprNewBeforeOverload1() {
|
||||
int *p = new int;
|
||||
} // expected-warning{{Memory is never released; potential leak}}
|
||||
|
||||
void testGlobalExprNewBeforeOverload2() {
|
||||
int *p = ::new int;
|
||||
} // expected-warning{{Memory is never released; potential leak}}
|
||||
|
||||
void testGlobalOpNewBeforeOverload() {
|
||||
//----- Standard non-placement operators
|
||||
void testGlobalOpNew() {
|
||||
void *p = operator new(0);
|
||||
} // 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;
|
||||
if (global != p)
|
||||
global = p;
|
||||
} // 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 *operator new(std::size_t, double d);
|
||||
void *operator new[](std::size_t);
|
||||
void *operator new[](std::size_t, double d);
|
||||
void testGlobalNewExprArray() {
|
||||
int *p = new int[0];
|
||||
} // expected-warning{{Memory is never released; potential leak}}
|
||||
|
||||
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 *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;
|
||||
} // expected-warning{{Memory is never released; potential leak}}
|
||||
void *p2 = operator new[](0, &i); // no warn
|
||||
|
||||
void testExprPlacementNewArray() {
|
||||
int i;
|
||||
int *p1 = new(&i) int[1]; // no warn - standard placement new[]
|
||||
int *p3 = new(&i) int; // no warn
|
||||
|
||||
int *p2 = new(1.0) int[1]; // no warn - overloaded placement new[]
|
||||
|
||||
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
|
||||
int *p4 = new(&i) int[0]; // no warn
|
||||
}
|
||||
|
||||
void testGlobalExprNew() {
|
||||
void *p = ::new int; // no warn - call to a custom new
|
||||
//----- Other cases
|
||||
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() {
|
||||
int *p = new int; // no warn - call to a custom new
|
||||
}
|
||||
struct PtrWrapper {
|
||||
int *x;
|
||||
|
||||
void testGlobalExprNewArray() {
|
||||
void *p = ::new int[1]; // no warn - call to a custom new
|
||||
}
|
||||
PtrWrapper(int *input) : x(input) {}
|
||||
};
|
||||
|
||||
void testOverloadedExprNewArray() {
|
||||
int *p = new int[1]; // no warn - call to a custom new
|
||||
void testNewInvalidationPlacement(PtrWrapper *w) {
|
||||
// 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
|
||||
void testMallocFreeNoWarn() {
|
||||
int i;
|
||||
free(&i); // no-warning
|
||||
free(&i); // no warn
|
||||
|
||||
int *p1 = (int *)malloc(sizeof(int));
|
||||
free(++p1); // no-warning
|
||||
free(++p1); // no warn
|
||||
|
||||
int *p2 = (int *)malloc(sizeof(int));
|
||||
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;
|
||||
free(p); // pointer escaped, no-warning
|
||||
}
|
||||
free(p);
|
||||
} // expected-warning{{Memory is never released; potential leak}}
|
||||
// FIXME: Pointer should escape
|
||||
|
||||
void testObjcFreeNewed() {
|
||||
int *p = new int;
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue