[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;
|
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.
|
||||||
|
|
|
@ -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>())
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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