Skip to content

Commit

Permalink
[analyzer] Add dispatch_data_create as a special case in RetainCountC…
Browse files Browse the repository at this point in the history
…hecker.

This function receives a callback block. The analyzer suspects that this block
may be used to take care of releasing the libdispatch object returned from
the function. In fact, it doesn't - it only releases the raw data buffer.
Inform the analyzer about that. Fixes the resulting false negatives.

rdar://problem/22280098

Differential Revision: https://reviews.llvm.org/D27409


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@289047 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
haoNoQ committed Dec 8, 2016
1 parent ba42896 commit 8b047f1
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
5 changes: 4 additions & 1 deletion lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,10 @@ void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S,
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();
}
}
Expand Down
80 changes: 80 additions & 0 deletions test/Analysis/retain-release-arc.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -fobjc-arc -fblocks -verify -Wno-objc-root-class %s -analyzer-output=text
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -fblocks -verify -Wno-objc-root-class %s -analyzer-output=text

typedef __typeof(sizeof(int)) size_t;

#define HAS_ARC __has_feature(objc_arc)

typedef unsigned long long CFOptionFlags;
Expand Down Expand Up @@ -45,6 +47,41 @@ - (void)dealloc;
@interface NSDictionary : NSObject
@end

#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 <OS_dispatch_object>
@end
@protocol OS_dispatch_queue <OS_dispatch_object>
@end

typedef NSObject<OS_dispatch_object> *dispatch_object_t;
typedef NSObject<OS_dispatch_data> *dispatch_data_t;
typedef NSObject<OS_dispatch_queue> *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);

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]; })


@interface SomeClass
@end

Expand Down Expand Up @@ -84,3 +121,46 @@ - (CFDictionaryRef)copyTestReturningCoreFoundation:(NSData *)plistData {
}
@end

int buf[1024];

void libdispatch_leaked_data() {
dispatch_data_t data = dispatch_data_create(buf, 1024,
dispatch_get_main_queue(), ^{});
}
#if !HAS_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 libdispatch_dispatch_released_data() {
dispatch_data_t data = dispatch_data_create(buf, 1024,
dispatch_get_main_queue(), ^{});
#if !HAS_ARC
dispatch_release(data); // no-warning
#endif
}

void libdispatch_objc_released_data() {
dispatch_data_t data = dispatch_data_create(buf, 1024,
dispatch_get_main_queue(), ^{});
#if !HAS_ARC
[data release]; // no-warning
#endif
}

void libdispatch_leaked_retained_data() {
dispatch_data_t data = dispatch_data_create(buf, 1024,
dispatch_get_main_queue(), ^{});
#if !HAS_ARC
dispatch_retain(data);
[data release];
#endif
}
#if !HAS_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

0 comments on commit 8b047f1

Please sign in to comment.