| // RUN: %clang_cc1 -analyze -analyzer-checker=osx.SecKeychainAPI %s -verify |
| |
| // 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 CFTypeRef; |
| typedef unsigned int UInt16; |
| 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() { |
| 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{{Only call free if a valid (non-NULL) buffer was returned}} |
| } // 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() { |
| 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() { |
| 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); |
| } |
| |
| void fooOnlyFree() { |
| unsigned int *ptr = 0; |
| OSStatus st = 0; |
| UInt32 length; |
| void *outData = &length; |
| SecKeychainItemFreeContent(ptr, outData);// expected-warning{{Trying to free data which has not been allocated}} |
| } |
| |
| // Do not warn if undefined value is passed to a function. |
| void fooOnlyFreeUndef() { |
| 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() { |
| 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; |
| }// no-warning |
| |
| void free(void *ptr); |
| void deallocateWithFree() { |
| 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); |
| extern void CFRelease(CFStringRef cf); |
| |
| 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() { |
| 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() { |
| 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_2() { |
| UInt32 pwdLen = 0; |
| void* pwdBytes = 0; |
| OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); |
| SecKeychainItemFreeContent(0, pwdBytes); // expected-warning {{Only call free if a valid (non-NULL) buffer was returned}} |
| } |
| |
| //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() { |
| 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() { |
| 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); |
| } |
| |