[analyzer] Fix Malloc False Positive (PR 12100)

When allocated buffer is passed to CF/NS..NoCopy functions, the
ownership is transfered unless the deallocator argument is set to
'kCFAllocatorNull'.

llvm-svn: 151608
This commit is contained in:
Anna Zaks 2012-02-28 01:54:22 +00:00
parent 16c4a972db
commit 06a77fc1b9
5 changed files with 89 additions and 21 deletions

View File

@ -1094,14 +1094,32 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
if (!SM.isInSystemHeader(D->getLocation())) if (!SM.isInSystemHeader(D->getLocation()))
return false; return false;
// Process C functions. // Process C/ObjC functions.
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
// White list the system functions whose arguments escape. // White list the system functions whose arguments escape.
const IdentifierInfo *II = FD->getIdentifier(); const IdentifierInfo *II = FD->getIdentifier();
if (II) { if (!II)
return true;
StringRef FName = II->getName(); StringRef FName = II->getName();
// White list thread local storage.
if (FName.equals("pthread_setspecific")) if (FName.equals("pthread_setspecific"))
return false; return false;
// White list the 'XXXNoCopy' ObjC Methods.
if (FName.endswith("NoCopy")) {
// Look for the deallocator argument. We know that the memory ownership
// is not transfered only if the deallocator argument is
// 'kCFAllocatorNull'.
for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts();
if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
StringRef DeallocatorName = DE->getFoundDecl()->getName();
if (DeallocatorName == "kCFAllocatorNull")
return true;
}
}
return false;
} }
// Otherwise, assume that the function does not free memory. // Otherwise, assume that the function does not free memory.

View File

@ -193,11 +193,14 @@ static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
// argument is const. // argument is const.
if (II) { if (II) {
StringRef FName = II->getName(); StringRef FName = II->getName();
// 'int pthread_setspecific(ptheread_key k, const void *)' stores a value // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
// into thread local storage. The value can later be retrieved with // value into thread local storage. The value can later be retrieved with
// 'void *ptheread_getspecific(pthread_key)'. So even thought the // 'void *ptheread_getspecific(pthread_key)'. So even thought the
// parameter is 'const void *', the region escapes through the call. // parameter is 'const void *', the region escapes through the call.
if (FName.equals("pthread_setspecific")) // - ObjC functions that end with "NoCopy" can free memory, of the passed
// in buffer.
if (FName == "pthread_setspecific" ||
FName.endswith("NoCopy"))
return; return;
} }

View File

@ -274,8 +274,8 @@ void DellocWithCFStringCreate2(CFAllocatorRef alloc) {
char * x; char * x;
st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes);
if (st == noErr) { if (st == noErr) {
CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorNull); CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorNull); // expected-warning{{Allocated data is not released}}
CFRelease(userStr); // expected-warning{{Allocated data is not released}} CFRelease(userStr);
} }
} }

View File

@ -61,3 +61,25 @@ void testNSDatafFreeWhenDoneFN(NSUInteger dataLength) {
NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:1]; NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:1];
free(data); // false negative free(data); // false negative
} }
// Test CF/NS...NoCopy. PR12100.
void testNSDatafFreeWhenDone(NSUInteger dataLength) {
CFStringRef str;
char *bytes = (char*)malloc(12);
str = CFStringCreateWithCStringNoCopy(0, bytes, NSNEXTSTEPStringEncoding, 0); // no warning
CFRelease(str); // default allocator also frees bytes
}
void stringWithExternalContentsExample(void) {
#define BufferSize 1000
CFMutableStringRef mutStr;
UniChar *myBuffer;
myBuffer = (UniChar *)malloc(BufferSize * sizeof(UniChar));
mutStr = CFStringCreateMutableWithExternalCharactersNoCopy(0, myBuffer, 0, BufferSize, kCFAllocatorNull); // expected-warning{{leak}}
CFRelease(mutStr);
//free(myBuffer);
}

View File

@ -1,9 +1,37 @@
#pragma clang system_header #pragma clang system_header
typedef unsigned int UInt32; typedef unsigned int UInt32;
typedef unsigned short UInt16;
typedef signed long CFIndex; typedef signed long CFIndex;
typedef signed char BOOL; typedef signed char BOOL;
typedef unsigned long NSUInteger; typedef unsigned long NSUInteger;
typedef unsigned short unichar;
typedef UInt16 UniChar;
enum {
NSASCIIStringEncoding = 1,
NSNEXTSTEPStringEncoding = 2,
NSJapaneseEUCStringEncoding = 3,
NSUTF8StringEncoding = 4,
NSISOLatin1StringEncoding = 5,
NSSymbolStringEncoding = 6,
NSNonLossyASCIIStringEncoding = 7,
};
typedef const struct __CFString * CFStringRef;
typedef struct __CFString * CFMutableStringRef;
typedef NSUInteger NSStringEncoding;
typedef UInt32 CFStringEncoding;
typedef const void * CFTypeRef;
typedef const struct __CFAllocator * CFAllocatorRef;
extern const CFAllocatorRef kCFAllocatorDefault;
extern const CFAllocatorRef kCFAllocatorSystemDefault;
extern const CFAllocatorRef kCFAllocatorMalloc;
extern const CFAllocatorRef kCFAllocatorMallocZone;
extern const CFAllocatorRef kCFAllocatorNull;
@class NSString, Protocol; @class NSString, Protocol;
extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
typedef struct _NSZone NSZone; typedef struct _NSZone NSZone;
@ -44,17 +72,7 @@ NSFastEnumerationState;
@end extern NSString * const NSBundleDidLoadNotification; @end extern NSString * const NSBundleDidLoadNotification;
typedef double NSTimeInterval; typedef double NSTimeInterval;
@interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate; @interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate;
@end typedef unsigned short unichar; @end
enum {
NSASCIIStringEncoding = 1,
NSNEXTSTEPStringEncoding = 2,
NSJapaneseEUCStringEncoding = 3,
NSUTF8StringEncoding = 4,
NSISOLatin1StringEncoding = 5,
NSSymbolStringEncoding = 6,
NSNonLossyASCIIStringEncoding = 7,
};
typedef NSUInteger NSStringEncoding;
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> @interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
- (NSUInteger)length; - (NSUInteger)length;
@ -73,3 +91,10 @@ typedef NSUInteger NSStringEncoding;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length; - (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b; - (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
@end @end
extern void CFRelease(CFTypeRef cf);
extern CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc, UniChar *chars, CFIndex numChars, CFIndex capacity, CFAllocatorRef externalCharactersAllocator);
extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator);
extern void CFStringAppend(CFMutableStringRef theString, CFStringRef appendedString);