retain/release checker: When generating summaries for CF/CG functions, allow arguments to "escape" if they are passed to a function containing the terms "InsertValue", "SetValue", or "AddValue". This fixes <rdar://problem/6539791>.

llvm-svn: 63341
This commit is contained in:
Ted Kremenek 2009-01-29 22:45:13 +00:00
parent ad89c410e6
commit ed90de4caa
2 changed files with 52 additions and 6 deletions

View File

@ -809,12 +809,27 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
if (isRelease(FD, FName+2))
S = getUnarySummary(FT, cfrelease);
else {
// For CoreFoundation and CoreGraphics functions we assume they
// follow the ownership idiom strictly and thus do not cause
// ownership to "escape".
assert (ScratchArgs.empty());
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing,
DoNothing);
// Remaining CoreFoundation and CoreGraphics functions.
// We use to assume that they all strictly followed the ownership idiom
// and that ownership cannot be transferred. While this is technically
// correct, many methods allow a tracked object to escape. For example:
//
// CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
// CFDictionaryAddValue(y, key, x);
// CFRelease(x);
// ... it is okay to use 'x' since 'y' has a reference to it
//
// We handle this and similar cases with the follow heuristic. If the
// function name contains "InsertValue", "SetValue" or "AddValue" then
// we assume that arguments may "escape."
//
ArgEffect E = (CStrInCStrNoCase(FName, "InsertValue") ||
CStrInCStrNoCase(FName, "AddValue") ||
CStrInCStrNoCase(FName, "SetValue"))
? MayEscape : DoNothing;
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E);
}
}
}

View File

@ -0,0 +1,31 @@
// RUN: clang -analyze -checker-cfref -analyzer-store-basic -verify %s &&
// RUN: clang -analyze -checker-cfref -analyzer-store-region -verify %s
typedef const struct __CFAllocator * CFAllocatorRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;
typedef signed long CFIndex;
typedef CFIndex CFNumberType;
typedef const void * CFTypeRef;
typedef struct {} CFDictionaryKeyCallBacks, CFDictionaryValueCallBacks;
typedef const struct __CFNumber * CFNumberRef;
extern const CFAllocatorRef kCFAllocatorDefault;
extern const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
extern const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
enum { kCFNumberSInt32Type = 3 };
CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
void CFDictionaryAddValue(CFMutableDictionaryRef theDict, const void *key, const void *value);
void CFRelease(CFTypeRef cf);
extern CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr);
void f(CFMutableDictionaryRef y, void* key, void* val_key) {
CFMutableDictionaryRef x = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(y, key, x);
CFRelease(x); // the dictionary keeps a reference, so the object isn't deallocated yet
signed z = 1;
CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &z);
if (value) {
CFDictionaryAddValue(x, val_key, value); // no-warning
CFRelease(value);
CFDictionaryAddValue(y, val_key, value); // no-warning
}
}