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:
		
							parent
							
								
									ad89c410e6
								
							
						
					
					
						commit
						ed90de4caa
					
				| 
						 | 
					@ -809,12 +809,27 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
 | 
				
			||||||
      if (isRelease(FD, FName+2))
 | 
					      if (isRelease(FD, FName+2))
 | 
				
			||||||
        S = getUnarySummary(FT, cfrelease);
 | 
					        S = getUnarySummary(FT, cfrelease);
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
        // For CoreFoundation and CoreGraphics functions we assume they
 | 
					        assert (ScratchArgs.empty());
 | 
				
			||||||
        // follow the ownership idiom strictly and thus do not cause
 | 
					        // Remaining CoreFoundation and CoreGraphics functions.
 | 
				
			||||||
        // ownership to "escape".
 | 
					        // We use to assume that they all strictly followed the ownership idiom
 | 
				
			||||||
        assert (ScratchArgs.empty());  
 | 
					        // and that ownership cannot be transferred.  While this is technically
 | 
				
			||||||
        S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, 
 | 
					        // correct, many methods allow a tracked object to escape.  For example:
 | 
				
			||||||
                                 DoNothing);
 | 
					        //
 | 
				
			||||||
 | 
					        //   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);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue