| // RUN: %clang_analyze_cc1 -analyzer-checker=osx.SecKeychainAPI -fblocks %s -verify | |
| #include "Inputs/system-header-simulator-objc.h" | |
| // Fake typedefs. | |
| typedef unsigned int OSStatus; | |
| typedef unsigned int SecKeychainAttributeList; | |
| typedef unsigned int SecKeychainItemRef; | |
| typedef unsigned int SecItemClass; | |
| typedef unsigned int UInt32; | |
| typedef unsigned int SecProtocolType; | |
| typedef unsigned int SecAuthenticationType; | |
| typedef unsigned int SecKeychainAttributeInfo; | |
| enum { | |
| noErr = 0, | |
| GenericError = 1 | |
| }; | |
| // Functions that allocate data. | |
| OSStatus SecKeychainItemCopyContent ( | |
| SecKeychainItemRef itemRef, | |
| SecItemClass *itemClass, | |
| SecKeychainAttributeList *attrList, | |
| UInt32 *length, | |
| void **outData | |
| ); | |
| OSStatus SecKeychainFindGenericPassword ( | |
| CFTypeRef keychainOrArray, | |
| UInt32 serviceNameLength, | |
| const char *serviceName, | |
| UInt32 accountNameLength, | |
| const char *accountName, | |
| UInt32 *passwordLength, | |
| void **passwordData, | |
| SecKeychainItemRef *itemRef | |
| ); | |
| OSStatus SecKeychainFindInternetPassword ( | |
| CFTypeRef keychainOrArray, | |
| UInt32 serverNameLength, | |
| const char *serverName, | |
| UInt32 securityDomainLength, | |
| const char *securityDomain, | |
| UInt32 accountNameLength, | |
| const char *accountName, | |
| UInt32 pathLength, | |
| const char *path, | |
| UInt16 port, | |
| SecProtocolType protocol, | |
| SecAuthenticationType authenticationType, | |
| UInt32 *passwordLength, | |
| void **passwordData, | |
| SecKeychainItemRef *itemRef | |
| ); | |
| OSStatus SecKeychainItemCopyAttributesAndData ( | |
| SecKeychainItemRef itemRef, | |
| SecKeychainAttributeInfo *info, | |
| SecItemClass *itemClass, | |
| SecKeychainAttributeList **attrList, | |
| UInt32 *length, | |
| void **outData | |
| ); | |
| // Functions which free data. | |
| OSStatus SecKeychainItemFreeContent ( | |
| SecKeychainAttributeList *attrList, | |
| void *data | |
| ); | |
| OSStatus SecKeychainItemFreeAttributesAndData ( | |
| SecKeychainAttributeList *attrList, | |
| void *data | |
| ); | |
| void errRetVal(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| if (st == GenericError) | |
| SecKeychainItemFreeContent(ptr, outData); | |
| } // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} | |
| // If null is passed in, the data is not allocated, so no need for the matching free. | |
| void fooDoNotReportNull(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 *length = 0; | |
| void **outData = 0; | |
| SecKeychainItemCopyContent(2, ptr, ptr, 0, 0); | |
| SecKeychainItemCopyContent(2, ptr, ptr, length, outData); | |
| }// no-warning | |
| void doubleAlloc(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); // expected-warning {{Allocated data should be released before another call to the allocator:}} | |
| if (st == noErr) | |
| SecKeychainItemFreeContent(ptr, outData); | |
| } | |
| // Do not warn if undefined value is passed to a function. | |
| void fooOnlyFreeUndef(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| SecKeychainItemFreeContent(ptr, outData); | |
| }// no-warning | |
| // Do not warn if the address is a parameter in the enclosing function. | |
| void fooOnlyFreeParam(void *attrList, void* X) { | |
| SecKeychainItemFreeContent(attrList, X); | |
| }// no-warning | |
| // If we are returning the value, do not report. | |
| void* returnContent(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| return outData; | |
| } // no-warning | |
| // Password was passed in as an argument and does not have to be deleted. | |
| OSStatus getPasswordAndItem(void** password, UInt32* passwordLength) { | |
| OSStatus err; | |
| SecKeychainItemRef item; | |
| err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", | |
| passwordLength, password, &item); | |
| return err; | |
| } // no-warning | |
| // Make sure we do not report an error if we call free only if password != 0. | |
| // Also, do not report double allocation if first allocation returned an error. | |
| OSStatus testSecKeychainFindGenericPassword(UInt32* passwordLength, | |
| CFTypeRef keychainOrArray, SecProtocolType protocol, | |
| SecAuthenticationType authenticationType) { | |
| OSStatus err; | |
| SecKeychainItemRef item; | |
| void *password; | |
| err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", | |
| passwordLength, &password, &item); | |
| if( err == GenericError ) { | |
| err = SecKeychainFindInternetPassword(keychainOrArray, | |
| 16, "server", 16, "domain", 16, "account", | |
| 16, "path", 222, protocol, authenticationType, | |
| passwordLength, &(password), 0); | |
| } | |
| if (err == noErr && password) { | |
| SecKeychainItemFreeContent(0, password); | |
| } | |
| return err; | |
| } | |
| int apiMismatch(SecKeychainItemRef itemRef, | |
| SecKeychainAttributeInfo *info, | |
| SecItemClass *itemClass) { | |
| OSStatus st = 0; | |
| SecKeychainAttributeList *attrList; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass, | |
| &attrList, &length, &outData); | |
| if (st == noErr) | |
| SecKeychainItemFreeContent(attrList, outData); // expected-warning{{Deallocator doesn't match the allocator}} | |
| return 0; | |
| } | |
| int ErrorCodesFromDifferentAPISDoNotInterfere(SecKeychainItemRef itemRef, | |
| SecKeychainAttributeInfo *info, | |
| SecItemClass *itemClass) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| OSStatus st2 = 0; | |
| SecKeychainAttributeList *attrList; | |
| UInt32 length2; | |
| void *outData2; | |
| st2 = SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass, | |
| &attrList, &length2, &outData2); | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| if (st == noErr) { | |
| SecKeychainItemFreeContent(ptr, outData); | |
| if (st2 == noErr) { | |
| SecKeychainItemFreeAttributesAndData(attrList, outData2); | |
| } | |
| } | |
| return 0; // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeAttributesAndData'}} | |
| } | |
| int foo(CFTypeRef keychainOrArray, SecProtocolType protocol, | |
| SecAuthenticationType authenticationType, SecKeychainItemRef *itemRef) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData[5]; | |
| st = SecKeychainFindInternetPassword(keychainOrArray, | |
| 16, "server", 16, "domain", 16, "account", | |
| 16, "path", 222, protocol, authenticationType, | |
| &length, &(outData[3]), itemRef); | |
| if (length == 5) { | |
| if (st == noErr) | |
| SecKeychainItemFreeContent(ptr, outData[3]); | |
| } | |
| if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} | |
| length++; | |
| } | |
| return 0; | |
| } | |
| int testErrorCodeAsLHS(CFTypeRef keychainOrArray, SecProtocolType protocol, | |
| SecAuthenticationType authenticationType, SecKeychainItemRef *itemRef) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainFindInternetPassword(keychainOrArray, | |
| 16, "server", 16, "domain", 16, "account", | |
| 16, "path", 222, protocol, authenticationType, | |
| &length, &outData, itemRef); | |
| if (noErr == st) | |
| SecKeychainItemFreeContent(ptr, outData); | |
| return 0; | |
| } | |
| void free(void *ptr); | |
| void deallocateWithFree(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| if (st == noErr) | |
| free(outData); // expected-warning{{Deallocator doesn't match the allocator: 'SecKeychainItemFreeContent' should be used}} | |
| } | |
| // Typesdefs for CFStringCreateWithBytesNoCopy. | |
| typedef char uint8_t; | |
| typedef signed long CFIndex; | |
| typedef UInt32 CFStringEncoding; | |
| typedef unsigned Boolean; | |
| typedef const struct __CFString * CFStringRef; | |
| 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; | |
| extern const CFAllocatorRef kCFAllocatorUseContext; | |
| CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator); | |
| void DellocWithCFStringCreate1(CFAllocatorRef alloc) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *bytes; | |
| char * x; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); | |
| if (st == noErr) { | |
| CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorDefault); // expected-warning{{Deallocator doesn't match the allocator:}} | |
| CFRelease(userStr); | |
| } | |
| } | |
| void DellocWithCFStringCreate2(CFAllocatorRef alloc) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *bytes; | |
| char * x; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); | |
| if (st == noErr) { | |
| CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorNull); // expected-warning{{Allocated data is not released}} | |
| CFRelease(userStr); | |
| } | |
| } | |
| void DellocWithCFStringCreate3(CFAllocatorRef alloc) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *bytes; | |
| char * x; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); | |
| if (st == noErr) { | |
| CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorUseContext); | |
| CFRelease(userStr); | |
| } | |
| } | |
| void DellocWithCFStringCreate4(CFAllocatorRef alloc) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *bytes; | |
| char * x; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); | |
| if (st == noErr) { | |
| CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, 0); // expected-warning{{Deallocator doesn't match the allocator:}} | |
| CFRelease(userStr); | |
| } | |
| } | |
| static CFAllocatorRef gKeychainDeallocator = 0; | |
| static CFAllocatorRef GetKeychainDeallocator(void) { | |
| return gKeychainDeallocator; | |
| } | |
| CFStringRef DellocWithCFStringCreate5(CFAllocatorRef alloc) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *bytes; | |
| char * x; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); | |
| if (st == noErr) { | |
| return CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, GetKeychainDeallocator()); // no-warning | |
| } | |
| return 0; | |
| } | |
| void radar10508828(void) { | |
| UInt32 pwdLen = 0; | |
| void* pwdBytes = 0; | |
| OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); | |
| #pragma unused(rc) | |
| if (pwdBytes) | |
| SecKeychainItemFreeContent(0, pwdBytes); | |
| } | |
| void radar10508828_20092614(void) { | |
| UInt32 pwdLen = 0; | |
| void* pwdBytes = 0; | |
| OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); | |
| SecKeychainItemFreeContent(0, pwdBytes); | |
| } | |
| //Example from bug 10797. | |
| __inline__ static | |
| const char *__WBASLLevelString(int level) { | |
| return "foo"; | |
| } | |
| static int *bug10798(int *p, int columns, int prevRow) { | |
| int *row = 0; | |
| row = p + prevRow * columns; | |
| prevRow += 2; | |
| do { | |
| ++prevRow; | |
| row+=columns; | |
| } while(10 >= row[1]); | |
| return row; | |
| } | |
| // Test inter-procedural behaviour. | |
| void my_FreeParam(void *attrList, void* X) { | |
| SecKeychainItemFreeContent(attrList, X); | |
| } | |
| void *my_AllocateReturn(OSStatus *st) { | |
| unsigned int *ptr = 0; | |
| UInt32 length; | |
| void *outData; | |
| *st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| return outData; | |
| } | |
| OSStatus my_Allocate_Param(void** password, UInt32* passwordLength) { | |
| OSStatus err; | |
| SecKeychainItemRef item; | |
| err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", | |
| passwordLength, password, &item); | |
| return err; | |
| } | |
| void allocAndFree1(void) { | |
| unsigned int *ptr = 0; | |
| OSStatus st = 0; | |
| UInt32 length; | |
| void *outData; | |
| st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); | |
| if (st == noErr) | |
| my_FreeParam(ptr, outData); | |
| } | |
| void consumeChar(char); | |
| void allocNoFree2(int x) { | |
| OSStatus st = 0; | |
| void *outData = my_AllocateReturn(&st); | |
| if (x) { | |
| consumeChar(*(char*)outData); // expected-warning{{Allocated data is not released:}} | |
| return; | |
| } else { | |
| consumeChar(*(char*)outData); | |
| } | |
| return; | |
| } | |
| void allocAndFree2(void *attrList) { | |
| OSStatus st = 0; | |
| void *outData = my_AllocateReturn(&st); | |
| if (st == noErr) | |
| my_FreeParam(attrList, outData); | |
| } | |
| void allocNoFree3(void) { | |
| UInt32 length = 32; | |
| void *outData; | |
| void *outData2; | |
| OSStatus st = my_Allocate_Param(&outData, &length); // expected-warning{{Allocated data is not released}} | |
| st = my_Allocate_Param(&outData2, &length); // expected-warning{{Allocated data is not released}} | |
| } | |
| void allocAndFree3(void *attrList) { | |
| UInt32 length = 32; | |
| void *outData; | |
| OSStatus st = my_Allocate_Param(&outData, &length); | |
| if (st == noErr) | |
| SecKeychainItemFreeContent(attrList, outData); | |
| } | |
| typedef struct AuthorizationValue { | |
| int length; | |
| void *data; | |
| } AuthorizationValue; | |
| typedef struct AuthorizationCallback { | |
| OSStatus (*SetContextVal)(AuthorizationValue *inValue); | |
| } AuthorizationCallback; | |
| static AuthorizationCallback cb; | |
| int radar_19196494(void) { | |
| @autoreleasepool { | |
| AuthorizationValue login_password = {}; | |
| UInt32 passwordLength; | |
| void *passwordData = 0; | |
| OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0); | |
| cb.SetContextVal(&login_password); | |
| if (err == noErr) { | |
| SecKeychainItemFreeContent(0, login_password.data); | |
| } | |
| } | |
| return 0; | |
| } | |
| int radar_19196494_v2(void) { | |
| @autoreleasepool { | |
| AuthorizationValue login_password = {}; | |
| OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0); | |
| if (!login_password.data) return 0; | |
| cb.SetContextVal(&login_password); | |
| if (err == noErr) { | |
| SecKeychainItemFreeContent(0, login_password.data); | |
| } | |
| } | |
| return 0; | |
| } |