forked from OSchip/llvm-project
[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:
parent
16c4a972db
commit
06a77fc1b9
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue