aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Yartsev <anton.yartsev@gmail.com>2013-03-28 16:10:38 +0000
committerAnton Yartsev <anton.yartsev@gmail.com>2013-03-28 16:10:38 +0000
commit697462881c4b9b704c7859f4bab0a6116c684bb1 (patch)
treeb80287ad1d017e8c8773445845370d61457ded97
parent829d187e2100d2cfd85acefc2e867d12336e393f (diff)
[analyzer] For now assume all standard global 'operator new' functions allocate memory in heap.
+ Improved test coverage for cplusplus.NewDelete checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178244 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp30
-rw-r--r--test/Analysis/NewDelete-checker-test.mm99
-rw-r--r--test/Analysis/NewDelete-custom.cpp57
-rw-r--r--test/Analysis/NewDelete-variadic.cpp19
5 files changed, 158 insertions, 56 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 068f9ce822..2598445866 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -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.
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 0fedf255d6..c1dd6b2220 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -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>())
diff --git a/test/Analysis/NewDelete-checker-test.mm b/test/Analysis/NewDelete-checker-test.mm
index 417e9770b8..64e9c82bb9 100644
--- a/test/Analysis/NewDelete-checker-test.mm
+++ b/test/Analysis/NewDelete-checker-test.mm
@@ -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;
+//----- Standard non-placement operators
+void testGlobalOpNew() {
+ void *p = operator new(0);
} // expected-warning{{Memory is never released; potential leak}}
-void testGlobalOpNewBeforeOverload() {
- void *p = operator new(0);
+void testGlobalOpNewArray() {
+ void *p = operator new[](0);
} // expected-warning{{Memory is never released; potential leak}}
-void testMemIsOnHeap() {
+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 testExprPlacementNew() {
- int i;
- int *p1 = new(&i) int; // no warn - standard placement new
+void testGlobalNewExprArray() {
+ int *p = new int[0];
+} // expected-warning{{Memory is never released; potential leak}}
- int *p2 = new(1.0) int; // no warn - overloaded placement new
+//----- Standard nothrow placement operators
+void testGlobalNoThrowPlacementOpNewBeforeOverload() {
+ void *p = operator new(0, std::nothrow);
+} // expected-warning{{Memory is never released; potential leak}}
- int *p3 = new (std::nothrow) int;
+void testGlobalNoThrowPlacementExprNewBeforeOverload() {
+ int *p = new(std::nothrow) int;
} // expected-warning{{Memory is never released; potential leak}}
-void testExprPlacementNewArray() {
+
+//----- Standard pointer placement operators
+void testGlobalPointerPlacementNew() {
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[]
+ void *p1 = operator new(0, &i); // no warn
- int *p3 = new (std::nothrow) int[1];
-} // expected-warning{{Memory is never released; potential leak}}
+ void *p2 = operator new[](0, &i); // no warn
-void testCustomOpNew() {
- void *p = operator new(0); // no warn - call to a custom new
-}
+ int *p3 = new(&i) int; // no warn
-void testGlobalExprNew() {
- void *p = ::new int; // no warn - call to a custom new
+ int *p4 = new(&i) int[0]; // no warn
}
-void testCustomExprNew() {
- int *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 testGlobalExprNewArray() {
- void *p = ::new int[1]; // no warn - call to a custom new
-}
+struct PtrWrapper {
+ int *x;
+
+ 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;
diff --git a/test/Analysis/NewDelete-custom.cpp b/test/Analysis/NewDelete-custom.cpp
new file mode 100644
index 0000000000..47105ff4ef
--- /dev/null
+++ b/test/Analysis/NewDelete-custom.cpp
@@ -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
+}
diff --git a/test/Analysis/NewDelete-variadic.cpp b/test/Analysis/NewDelete-variadic.cpp
new file mode 100644
index 0000000000..4b02403c70
--- /dev/null
+++ b/test/Analysis/NewDelete-variadic.cpp
@@ -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
+}