Index: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -953,7 +953,10 @@ if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) { // When the CGBitmapContext is deallocated, the callback here will free // the associated data buffer. - if (Name->isStr("CGBitmapContextCreateWithData")) + // The callback in dispatch_data_create frees the buffer, but not + // the data object. + if (Name->isStr("CGBitmapContextCreateWithData") || + Name->isStr("dispatch_data_create")) RE = S->getRetEffect(); } } @@ -1134,6 +1137,10 @@ // the context. ScratchArgs = AF.add(ScratchArgs, 5, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "dispatch_retain") { + S = getUnarySummary(FT, cfretain); + } else if (FName == "dispatch_release") { + S = getUnarySummary(FT, cfrelease); } else if (FName == "dispatch_set_context" || FName == "xpc_connection_set_context") { // - The analyzer currently doesn't have Index: test/Analysis/dispatch-data-leak.m =================================================================== --- /dev/null +++ test/Analysis/dispatch-data-leak.m @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -w -triple x86_64-apple-macosx10.12.0 -fblocks -analyze -analyzer-output=text -analyzer-checker=core,osx.cocoa,unix.Malloc -verify %s +// RUN: %clang_cc1 -w -triple x86_64-apple-macosx10.12.0 -fblocks -fobjc-arc -analyze -analyzer-output=text -analyzer-checker=core,osx.cocoa,unix.Malloc -verify %s +// RUN: %clang_cc1 -w -triple x86_64-apple-macosx10.12.0 -fblocks -DDISPATCH_MACROS -analyze -analyzer-output=text -analyzer-checker=core,osx.cocoa,unix.Malloc -verify %s +// RUN: %clang_cc1 -w -triple x86_64-apple-macosx10.12.0 -fblocks -DDISPATCH_MARCOS -fobjc-arc -analyze -analyzer-output=text -analyzer-checker=core,osx.cocoa,unix.Malloc -verify %s + +#include "Inputs/system-header-simulator.h" +#include "Inputs/system-header-simulator-objc.h" + +#define NON_ARC !__has_feature(objc_arc) + +#if !NON_ARC + // expected-no-diagnostics +#endif + +#define OS_OBJECT_RETURNS_RETAINED __attribute__((__ns_returns_retained__)) +#define DISPATCH_RETURNS_RETAINED OS_OBJECT_RETURNS_RETAINED + +@protocol OS_dispatch_object +@end +@protocol OS_dispatch_data +@end +@protocol OS_dispatch_queue +@end + +typedef NSObject *dispatch_object_t; +typedef NSObject *dispatch_data_t; +typedef NSObject *dispatch_queue_t; + +typedef void (^dispatch_block_t)(void); + +dispatch_queue_t dispatch_get_main_queue(void); + +DISPATCH_RETURNS_RETAINED dispatch_data_t +dispatch_data_create(const void *buffer, size_t size, + dispatch_queue_t _Nullable queue, + dispatch_block_t _Nullable destructor); + +#ifdef DISPATCH_MACROS +void _dispatch_object_validate(dispatch_object_t object); +#define dispatch_retain(object) \ + __extension__({ dispatch_object_t _o = (object); \ + _dispatch_object_validate(_o); \ + (void)[_o retain]; }) +#define dispatch_release(object) \ + __extension__({ dispatch_object_t _o = (object); \ + _dispatch_object_validate(_o); \ + [_o release]; }) +#else +void dispatch_retain(dispatch_object_t object); +void dispatch_release(dispatch_object_t object); +#endif + + int buf[1024]; + +void leaked_data() { + dispatch_data_t data = dispatch_data_create(buf, 1024, + dispatch_get_main_queue(), ^{}); +} +#if NON_ARC + // expected-warning@-2{{Potential leak of an object stored into 'data'}} + // expected-note@-5{{Call to function 'dispatch_data_create' returns an Objective-C object with a +1 retain count}} + // expected-note@-4{{Object leaked: object allocated and stored into 'data' is not referenced later in this execution path and has a retain count of +1}} +#endif + +void dispatch_released_data() { + dispatch_data_t data = dispatch_data_create(buf, 1024, + dispatch_get_main_queue(), ^{}); +#if NON_ARC + dispatch_release(data); // no-warning +#endif +} + +void objc_released_data() { + dispatch_data_t data = dispatch_data_create(buf, 1024, + dispatch_get_main_queue(), ^{}); +#if NON_ARC + [data release]; // no-warning +#endif +} + +void leaked_retained_data() { + dispatch_data_t data = dispatch_data_create(buf, 1024, + dispatch_get_main_queue(), ^{}); +#if NON_ARC + dispatch_retain(data); + [data release]; +#endif +} +#if NON_ARC +// expected-warning@-2{{Potential leak of an object stored into 'data'}} +// expected-note@-9{{Call to function 'dispatch_data_create' returns an Objective-C object with a +1 retain count}} +// expected-note@-7{{Reference count incremented. The object now has a +2 retain count}} +// expected-note@-7{{Reference count decremented. The object now has a +1 retain count}} +// expected-note@-6{{Object leaked: object allocated and stored into 'data' is not referenced later in this execution path and has a retain count of +1}} +#endif