| // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s |
| #import "Inputs/system-header-simulator-objc.h" |
| #import "Inputs/system-header-simulator-for-malloc.h" |
| |
| // Done with headers. Start testing. |
| void testNSDatafFreeWhenDoneNoError(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength]; |
| } |
| |
| void testNSDataFreeWhenDoneYES(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
| } |
| |
| void testNSDataFreeWhenDoneYES2(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
| } |
| |
| void testNSDataFreeWhenDoneYES2_with_wrapper(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| Wrapper *nsdata = [[Wrapper alloc] initWithBytesNoCopy:data length:dataLength]; // no-warning |
| } |
| |
| void testNSStringFreeWhenDoneYES3(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:1]; |
| } |
| |
| void testNSStringFreeWhenDoneYES4(NSUInteger dataLength) { |
| unichar *data = (unichar*)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:1]; |
| free(data); //expected-warning {{Attempt to free non-owned memory}} |
| } |
| |
| void testNSStringFreeWhenDoneYES(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:1]; // no-warning |
| } |
| |
| void testNSStringFreeWhenDoneYES2(NSUInteger dataLength) { |
| unichar *data = (unichar*)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
| } |
| |
| |
| void testNSDataFreeWhenDoneNO(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| void testNSDataFreeWhenDoneNO2(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| |
| void testNSStringFreeWhenDoneNO(NSUInteger dataLength) { |
| unsigned char *data = (unsigned char *)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) { |
| unichar *data = (unichar*)malloc(42); |
| NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| void testOffsetFree() { |
| int *p = (int *)malloc(sizeof(int)); |
| NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to +dataWithBytesNoCopy:length:freeWhenDone: is offset by 4 bytes from the start of memory allocated by malloc()}} |
| } |
| |
| void testRelinquished1() { |
| void *data = malloc(42); |
| NSData *nsdata = [NSData dataWithBytesNoCopy:data length:42 freeWhenDone:1]; |
| free(data); // expected-warning {{Attempt to free non-owned memory}} |
| } |
| |
| void testRelinquished2() { |
| void *data = malloc(42); |
| NSData *nsdata; |
| free(data); |
| [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Use of memory after it is freed}} |
| } |
| |
| @interface My |
| + (void)param:(void *)p; |
| @end |
| |
| void testUseAfterFree() { |
| int *p = (int *)malloc(sizeof(int)); |
| free(p); |
| [My param:p]; // expected-warning{{Use of memory after it is freed}} |
| } |
| |
| void testNoCopy() { |
| char *p = (char *)calloc(sizeof(int), 1); |
| CustomData *w = [CustomData somethingNoCopy:p]; // no-warning |
| } |
| |
| void testFreeWhenDone() { |
| char *p = (char *)calloc(sizeof(int), 1); |
| CustomData *w = [CustomData something:p freeWhenDone:1]; // no-warning |
| } |
| |
| void testFreeWhenDonePositive() { |
| char *p = (char *)calloc(sizeof(int), 1); |
| CustomData *w = [CustomData something:p freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| void testFreeWhenDoneNoCopy() { |
| int *p = (int *)malloc(sizeof(int)); |
| CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:1]; // no-warning |
| } |
| |
| void testFreeWhenDoneNoCopyPositive() { |
| int *p = (int *)malloc(sizeof(int)); |
| CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:0]; // expected-warning{{leak}} |
| } |
| |
| // Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided. |
| 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); |
| } |
| |
| // PR12101 : pointers can escape through custom deallocators set on creation of a container. |
| void TestCallbackReleasesMemory(CFDictionaryKeyCallBacks keyCallbacks) { |
| void *key = malloc(12); |
| void *val = malloc(12); |
| CFMutableDictionaryRef x = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks); |
| CFDictionarySetValue(x, key, val); |
| return;// no-warning |
| } |
| |
| NSData *radar10976702() { |
| void *bytes = malloc(10); |
| return [NSData dataWithBytesNoCopy:bytes length:10]; // no-warning |
| } |
| |
| void testBlocks() { |
| int *x= (int*)malloc(sizeof(int)); |
| int (^myBlock)(int) = ^(int num) { |
| free(x); |
| return num; |
| }; |
| myBlock(3); |
| } |
| |
| // Test NSMapInsert. |
| @interface NSMapTable : NSObject <NSCopying, NSCoding, NSFastEnumeration> |
| @end |
| extern void *NSMapGet(NSMapTable *table, const void *key); |
| extern void NSMapInsert(NSMapTable *table, const void *key, const void *value); |
| extern void NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value); |
| char *strdup(const char *s); |
| |
| NSString * radar11152419(NSString *string1, NSMapTable *map) { |
| const char *strkey = "key"; |
| NSString *string = ( NSString *)NSMapGet(map, strkey); |
| if (!string) { |
| string = [string1 copy]; |
| NSMapInsert(map, strdup(strkey), (void*)string); // no warning |
| NSMapInsertKnownAbsent(map, strdup(strkey), (void*)string); // no warning |
| } |
| return string; |
| } |
| |
| // Test that we handle pointer escaping through OSAtomicEnqueue. |
| typedef volatile struct { |
| void *opaque1; |
| long opaque2; |
| } OSQueueHead; |
| extern "C" void OSAtomicEnqueue( OSQueueHead *__list, void *__new, size_t __offset) __attribute__((weak_import)); |
| static inline void radar11111210(OSQueueHead *pool) { |
| void *newItem = malloc(4); |
| OSAtomicEnqueue(pool, newItem, 4); |
| } |
| |
| // Pointer might escape through CGDataProviderCreateWithData (radar://11187558). |
| typedef struct CGDataProvider *CGDataProviderRef; |
| typedef void (*CGDataProviderReleaseDataCallback)(void *info, const void *data, |
| size_t size); |
| extern CGDataProviderRef CGDataProviderCreateWithData(void *info, |
| const void *data, size_t size, |
| CGDataProviderReleaseDataCallback releaseData) |
| __attribute__((visibility("default"))); |
| void *calloc(size_t, size_t); |
| |
| static void releaseDataCallback (void *info, const void *data, size_t size) { |
| #pragma unused (info, size) |
| free((void*)data); |
| } |
| void testCGDataProviderCreateWithData() { |
| void* b = calloc(8, 8); |
| CGDataProviderRef p = CGDataProviderCreateWithData(0, b, 8*8, releaseDataCallback); |
| } |
| |
| // Assume that functions which take a function pointer can free memory even if |
| // they are defined in system headers and take the const pointer to the |
| // allocated memory. (radar://11160612) |
| extern CGDataProviderRef UnknownFunWithCallback(void *info, |
| const void *data, size_t size, |
| CGDataProviderReleaseDataCallback releaseData) |
| __attribute__((visibility("default"))); |
| void testUnknownFunWithCallBack() { |
| void* b = calloc(8, 8); |
| CGDataProviderRef p = UnknownFunWithCallback(0, b, 8*8, releaseDataCallback); |
| } |
| |
| // Test blocks. |
| void acceptBlockParam(void *, void (^block)(void *), unsigned); |
| void testCallWithBlockCallback() { |
| void *l = malloc(12); |
| acceptBlockParam(l, ^(void *i) { free(i); }, sizeof(char *)); |
| } |
| |
| // Test blocks in system headers. |
| void testCallWithBlockCallbackInSystem() { |
| void *l = malloc(12); |
| SystemHeaderFunctionWithBlockParam(l, ^(void *i) { free(i); }, sizeof(char *)); |
| } |
| |
| // Test escape into NSPointerArray. radar://11691035, PR13140 |
| void foo(NSPointerArray* pointerArray) { |
| |
| void* p1 = malloc (1024); |
| if (p1) { |
| [pointerArray addPointer:p1]; |
| } |
| |
| void* p2 = malloc (1024); |
| if (p2) { |
| [pointerArray insertPointer:p2 atIndex:1]; |
| } |
| |
| void* p3 = malloc (1024); |
| if (p3) { |
| [pointerArray replacePointerAtIndex:1 withPointer:p3]; |
| } |
| |
| // Freeing the buffer is allowed. |
| void* buffer = [pointerArray pointerAtIndex:0]; |
| free(buffer); |
| } |
| |
| void noCrashOnVariableArgumentSelector() { |
| NSMutableString *myString = [NSMutableString stringWithString:@"some text"]; |
| [myString appendFormat:@"some text = %d", 3]; |
| } |
| |
| void test12365078_check() { |
| unichar *characters = (unichar*)malloc(12); |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string) free(characters); // no-warning |
| } |
| |
| void test12365078_nocheck() { |
| unichar *characters = (unichar*)malloc(12); |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| } |
| |
| void test12365078_false_negative() { |
| unichar *characters = (unichar*)malloc(12); |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string) {;} |
| } |
| |
| void test12365078_no_malloc(unichar *characters) { |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string) {free(characters);} |
| } |
| |
| NSString *test12365078_no_malloc_returnValue(unichar *characters) { |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string) { |
| return 0; // no-warning |
| } |
| return string; |
| } |
| |
| void test12365078_nocheck_nomalloc(unichar *characters) { |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| free(characters); // expected-warning {{Attempt to free non-owned memory}} |
| } |
| |
| void test12365078_nested(unichar *characters) { |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string) { |
| NSString *string2 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string2) { |
| NSString *string3 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string3) { |
| NSString *string4 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (!string4) |
| free(characters); |
| } |
| } |
| } |
| } |
| |
| void test12365078_check_positive() { |
| unichar *characters = (unichar*)malloc(12); |
| NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
| if (string) free(characters); // expected-warning{{Attempt to free non-owned memory}} |
| } |
| |
| void *test_reinterpret_cast_to_block() { |
| // Used to leak because the pointer was disappearing |
| // during the reinterpret_cast. |
| using BlockPtrTy = void (^)(); |
| struct Block {}; |
| Block* block = static_cast<Block*>(malloc(sizeof(Block))); |
| BlockPtrTy blockPtr = reinterpret_cast<BlockPtrTy>(block); // no-warning |
| return blockPtr; |
| } |