From: Jordan Rose Date: Fri, 5 Apr 2013 17:55:00 +0000 (+0000) Subject: [analyzer] Split new/delete checker into use-after-free and leaks parts. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e85deb356f5d2d2172b7ef70314bc9cfc742a936;p=clang [analyzer] Split new/delete checker into use-after-free and leaks parts. This splits the leak-checking part of alpha.cplusplus.NewDelete into a separate user-level checker, alpha.cplusplus.NewDeleteLeaks. All the difficult false positives we've seen with the new/delete checker have been spurious leak warnings; the use-after-free warnings and mismatched deallocator warnings, while rare, have always been valid. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178890 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 3db3fb9962..a29f53bb79 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -176,7 +176,11 @@ def VirtualCallChecker : Checker<"VirtualCall">, DescFile<"VirtualCallChecker.cpp">; def NewDeleteChecker : Checker<"NewDelete">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by new/delete.">, + HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, + DescFile<"MallocChecker.cpp">; + +def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; } // end: "alpha.cplusplus" diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 318be5bf10..851aa0ca36 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -164,6 +164,7 @@ public: DefaultBool CMallocPessimistic; DefaultBool CMallocOptimistic; DefaultBool CNewDeleteChecker; + DefaultBool CNewDeleteLeaksChecker; DefaultBool CMismatchedDeallocatorChecker; }; @@ -1536,12 +1537,21 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + !Filter.CNewDeleteLeaksChecker) return; - if (!isTrackedFamily(C, Sym)) + const RefState *RS = C.getState()->get(Sym); + assert(RS && "cannot leak an untracked symbol"); + AllocationFamily Family = RS->getAllocationFamily(); + if (!isTrackedFamily(Family)) return; + // Special case for new and new[]; these are controlled by a separate checker + // flag so that they can be selectively disabled. + if (Family == AF_CXXNew || Family == AF_CXXNewArray) + if (!Filter.CNewDeleteLeaksChecker) + return; + assert(N); if (!BT_Leak) { BT_Leak.reset(new BugType("Memory leak", "Memory Error")); @@ -2115,4 +2125,5 @@ void ento::register##name(CheckerManager &mgr) {\ REGISTER_CHECKER(MallocPessimistic) REGISTER_CHECKER(MallocOptimistic) REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(NewDeleteLeaksChecker) REGISTER_CHECKER(MismatchedDeallocatorChecker) diff --git a/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp b/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp index b0bb1735b4..22742f4ed3 100644 --- a/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp +++ b/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,alpha.cplusplus.NewDelete -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); @@ -52,7 +53,10 @@ void testNewDoubleFree() { void testNewLeak() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}} +} +#ifdef LEAKS +// expected-warning@-2 {{Memory is never released; potential leak of memory pointed to by 'p'}} +#endif void testNewUseAfterFree() { int *p = (int *)operator new(0); diff --git a/test/Analysis/Malloc+NewDelete_intersections.cpp b/test/Analysis/Malloc+NewDelete_intersections.cpp index 7a0ef8e13c..272813d47b 100644 --- a/test/Analysis/Malloc+NewDelete_intersections.cpp +++ b/test/Analysis/Malloc+NewDelete_intersections.cpp @@ -1,11 +1,12 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,alpha.cplusplus.NewDelete -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -std=c++11 -verify %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); //------------------------------------------------------------------- -// Check that unix.Malloc + alpha.cplusplus.NewDelete does not enable +// Check that unix.Malloc + cplusplus.NewDelete does not enable // warnings produced by unix.MismatchedDeallocator. //------------------------------------------------------------------- void testMismatchedDeallocator() { diff --git a/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp b/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp index 23b70b89fb..84ecbaddc7 100644 --- a/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp +++ b/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.MismatchedDeallocator -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.MismatchedDeallocator -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.MismatchedDeallocator -DLEAKS -std=c++11 -verify %s // expected-no-diagnostics typedef __typeof(sizeof(int)) size_t; diff --git a/test/Analysis/NewDelete-checker-test.cpp b/test/Analysis/NewDelete-checker-test.cpp index c31d7f3032..74116fb7bc 100644 --- a/test/Analysis/NewDelete-checker-test.cpp +++ b/test/Analysis/NewDelete-checker-test.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" typedef __typeof__(sizeof(int)) size_t; @@ -12,28 +13,46 @@ int *global; //----- Standard non-placement operators void testGlobalOpNew() { void *p = operator new(0); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testGlobalOpNewArray() { void *p = operator new[](0); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testGlobalNewExpr() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testGlobalNewExprArray() { int *p = new int[0]; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif //----- Standard nothrow placement operators void testGlobalNoThrowPlacementOpNewBeforeOverload() { void *p = operator new(0, std::nothrow); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testGlobalNoThrowPlacementExprNewBeforeOverload() { int *p = new(std::nothrow) int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif //----- Standard pointer placement operators diff --git a/test/Analysis/NewDelete-custom.cpp b/test/Analysis/NewDelete-custom.cpp index 7d7796bccb..a4665a1757 100644 --- a/test/Analysis/NewDelete-custom.cpp +++ b/test/Analysis/NewDelete-custom.cpp @@ -1,6 +1,12 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.Malloc -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.Malloc -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" +#ifndef LEAKS +// expected-no-diagnostics +#endif + + void *allocator(std::size_t size); void *operator new[](std::size_t size) throw() { return allocator(size); } @@ -19,7 +25,10 @@ void testNewMethod() { C *p2 = new C; // no warn C *c3 = ::new C; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testOpNewArray() { void *p = operator new[](0); // call is inlined, no warn @@ -27,7 +36,11 @@ void testOpNewArray() { void testNewExprArray() { int *p = new int[0]; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif + //----- Custom non-placement operators void testOpNew() { @@ -36,16 +49,26 @@ void testOpNew() { void testNewExpr() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif + //----- Custom NoThrow placement operators void testOpNewNoThrow() { void *p = operator new(0, std::nothrow); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif void testNewExprNoThrow() { int *p = new(std::nothrow) int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Memory is never released; potential leak}} +#endif //----- Custom placement operators void testOpNewPlacement() { diff --git a/test/Analysis/NewDelete-intersections.mm b/test/Analysis/NewDelete-intersections.mm index 3a87e4f3f1..247fef80af 100644 --- a/test/Analysis/NewDelete-intersections.mm +++ b/test/Analysis/NewDelete-intersections.mm @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -std=c++11 -DLEAKS -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" #include "Inputs/system-header-simulator-objc.h" @@ -39,16 +40,25 @@ void testDeleteMalloced() { void testFreeOpNew() { void *p = operator new(0); free(p); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2 {{Memory is never released; potential leak}} +#endif void testFreeNewExpr() { int *p = new int; free(p); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2 {{Memory is never released; potential leak}} +#endif void testObjcFreeNewed() { int *p = new int; - NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Memory is never released; potential leak}} + NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; +#ifdef LEAKS + // expected-warning@-2 {{Memory is never released; potential leak}} +#endif } void testFreeAfterDelete() { diff --git a/test/Analysis/NewDelete-variadic.cpp b/test/Analysis/NewDelete-variadic.cpp index 129af1f9c6..02d6a9abce 100644 --- a/test/Analysis/NewDelete-variadic.cpp +++ b/test/Analysis/NewDelete-variadic.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.Malloc -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -fblocks -verify %s // expected-no-diagnostics namespace std {