[cfe-commits] r151737 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h lib/StaticAnalyzer/Checkers/MallocChecker.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/malloc.c test/Analysis/malloc.mm test/Analysis/system-header-simulator-objc.h test/Analysis/system-header-simulator.h

Anna Zaks ganna at apple.com
Wed Feb 29 10:42:47 PST 2012


Author: zaks
Date: Wed Feb 29 12:42:47 2012
New Revision: 151737

URL: http://llvm.org/viewvc/llvm-project?rev=151737&view=rev
Log:
[analyzer] Malloc: A pointer might escape through CFContainers APIs,
funopen, setvbuf.

Teach the checker and the engine about these APIs to resolve malloc
false positives. As I am adding more of these APIs, it is clear that all
this should be factored out into a separate callback (for example,
region escapes). Malloc, KeyChainAPI and RetainRelease checkers could
all use it.

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
    cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
    cfe/trunk/test/Analysis/malloc.c
    cfe/trunk/test/Analysis/malloc.mm
    cfe/trunk/test/Analysis/system-header-simulator-objc.h
    cfe/trunk/test/Analysis/system-header-simulator.h

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h Wed Feb 29 12:42:47 2012
@@ -21,9 +21,11 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringExtras.h"
 
 namespace clang {
 namespace ento {
+using llvm::StrInStrNoCase;
 
 /// \brief Represents both explicit ObjC message expressions and implicit
 /// messages that are sent for handling properties in dot syntax.
@@ -254,6 +256,33 @@
     assert(isObjCMessage());
     return Msg.getReceiverSourceRange();
   }
+
+  /// \brief Check if the name corresponds to a CoreFoundation or CoreGraphics 
+  /// function that allows objects to escape.
+  ///
+  /// Many methods allow a tracked object to escape.  For example:
+  ///
+  ///   CFMutableDictionaryRef x = CFDictionaryCreateMutable(..., customDeallocator);
+  ///   CFDictionaryAddValue(y, key, x);
+  ///
+  /// We handle this and similar cases with the following heuristic.  If the
+  /// function name contains "InsertValue", "SetValue", "AddValue",
+  /// "AppendValue", or "SetAttribute", then we assume that arguments may
+  /// escape.
+  //
+  // TODO: To reduce false negatives here, we should track the container
+  // allocation site and check if a proper deallocator was set there.
+  static bool isCFCGAllowingEscape(StringRef FName) {
+    if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
+           if (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
+               StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+               StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+               StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
+               StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) {
+         return true;
+       }
+    return false;
+  }
 };
 
 }

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Wed Feb 29 12:42:47 2012
@@ -42,10 +42,8 @@
   RefState(Kind k, const Stmt *s) : K(k), S(s) {}
 
   bool isAllocated() const { return K == AllocateUnchecked; }
-  //bool isFailed() const { return K == AllocateFailed; }
   bool isReleased() const { return K == Released; }
-  //bool isEscaped() const { return K == Escaped; }
-  //bool isRelinquished() const { return K == Relinquished; }
+
   const Stmt *getStmt() const { return S; }
 
   bool operator==(const RefState &X) const {
@@ -1122,6 +1120,43 @@
       return false;
     }
 
+    // PR12101
+    // Many CoreFoundation and CoreGraphics might allow a tracked object 
+    // to escape.
+    if (Call->isCFCGAllowingEscape(FName))
+      return false;
+
+    // Associating streams with malloced buffers. The pointer can escape if
+    // 'closefn' is specified (and if that function does free memory).
+    // Currently, we do not inspect the 'closefn' function (PR12101).
+    if (FName == "funopen")
+      if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
+        return false;
+
+    // Do not warn on pointers passed to 'setbuf' when used with std streams,
+    // these leaks might be intentional when setting the buffer for stdio.
+    // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
+    if (FName == "setbuf" || FName =="setbuffer" ||
+        FName == "setlinebuf" || FName == "setvbuf") {
+      if (Call->getNumArgs() >= 1)
+        if (const DeclRefExpr *Arg =
+              dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts()))
+          if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl()))
+              if (D->getCanonicalDecl()->getName().find("std")
+                                                   != StringRef::npos)
+                return false;
+    }
+
+    // A bunch of other functions, which take ownership of a pointer (See retain
+    // release checker). Not all the parameters here are invalidated, but the
+    // Malloc checker cannot differentiate between them. The right way of doing
+    // this would be to implement a pointer escapes callback.
+    if (FName == "CVPixelBufferCreateWithBytes" ||
+        FName == "CGBitmapContextCreateWithData" ||
+        FName == "CVPixelBufferCreateWithPlanarBytes") {
+      return false;
+    }
+
     // Otherwise, assume that the function does not free memory.
     // Most system calls, do not free the memory.
     return true;

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Wed Feb 29 12:42:47 2012
@@ -197,10 +197,15 @@
       // value into thread local storage. The value can later be retrieved with
       // 'void *ptheread_getspecific(pthread_key)'. So even thought the
       // parameter is 'const void *', the region escapes through the call.
+      //  - funopen - sets a buffer for future IO calls.
       //  - ObjC functions that end with "NoCopy" can free memory, of the passed
       // in buffer.
+      // - Many CF containers allow objects to escape through custom
+      // allocators/deallocators upon container construction.
       if (FName == "pthread_setspecific" ||
-          FName.endswith("NoCopy"))
+          FName == "funopen" ||
+          FName.endswith("NoCopy") ||
+          Call.isCFCGAllowingEscape(FName))
         return;
     }
 

Modified: cfe/trunk/test/Analysis/malloc.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc.c?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/malloc.c (original)
+++ cfe/trunk/test/Analysis/malloc.c Wed Feb 29 12:42:47 2012
@@ -677,7 +677,9 @@
   free(s2);
 }
 
+// ----------------------------------------------------------------------------
 // Test the system library functions to which the pointer can escape.
+// This tests false positive suppression.
 
 // For now, we assume memory passed to pthread_specific escapes.
 // TODO: We could check that if a new pthread binding is set, the existing
@@ -687,6 +689,46 @@
   pthread_setspecific(key, buf); // no warning
 }
 
+// PR12101: Test funopen().
+static int releasePtr(void *_ctx) {
+    free(_ctx);
+    return 0;
+}
+FILE *useFunOpen() {
+    void *ctx = malloc(sizeof(int));
+    FILE *f = funopen(ctx, 0, 0, 0, releasePtr); // no warning
+    if (f == 0) {
+        free(ctx);
+    }
+    return f;
+}
+FILE *useFunOpenNoReleaseFunction() {
+    void *ctx = malloc(sizeof(int));
+    FILE *f = funopen(ctx, 0, 0, 0, 0);
+    if (f == 0) {
+        free(ctx);
+    }
+    return f; // expected-warning{{leak}}
+}
+
+// Test setbuf, setvbuf.
+int my_main_no_warning() {
+    char *p = malloc(100);
+    setvbuf(stdout, p, 0, 100);
+    return 0;
+}
+int my_main_no_warning2() {
+    char *p = malloc(100);
+    setbuf(__stdoutp, p);
+    return 0;
+}
+int my_main_warn(FILE *f) {
+    char *p = malloc(100);
+    setvbuf(f, p, 0, 100);
+    return 0;// expected-warning {{leak}}
+}
+
+// ----------------------------------------------------------------------------
 // Below are the known false positives.
 
 // TODO: There should be no warning here. This one might be difficult to get rid of.
@@ -706,6 +748,7 @@
   return;
 }
 
+// ----------------------------------------------------------------------------
 // False negatives.
 
 // TODO: This requires tracking symbols stored inside the structs/arrays.

Modified: cfe/trunk/test/Analysis/malloc.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc.mm?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/malloc.mm (original)
+++ cfe/trunk/test/Analysis/malloc.mm Wed Feb 29 12:42:47 2012
@@ -62,8 +62,7 @@
   free(data); // false negative
 }
 
-// Test CF/NS...NoCopy. PR12100.
-
+// Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided.
 void testNSDatafFreeWhenDone(NSUInteger dataLength) {
   CFStringRef str;
   char *bytes = (char*)malloc(12);
@@ -83,3 +82,12 @@
     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
+}

Modified: cfe/trunk/test/Analysis/system-header-simulator-objc.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/system-header-simulator-objc.h?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/system-header-simulator-objc.h (original)
+++ cfe/trunk/test/Analysis/system-header-simulator-objc.h Wed Feb 29 12:42:47 2012
@@ -92,6 +92,19 @@
 - (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
 @end
 
+typedef struct {
+}
+CFDictionaryKeyCallBacks;
+extern const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
+typedef struct {
+}
+CFDictionaryValueCallBacks;
+extern const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
+typedef const struct __CFDictionary * CFDictionaryRef;
+typedef struct __CFDictionary * CFMutableDictionaryRef;
+extern CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
+void CFDictionarySetValue(CFMutableDictionaryRef, const void *, const void *);
+
 
 extern void CFRelease(CFTypeRef cf);
 

Modified: cfe/trunk/test/Analysis/system-header-simulator.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/system-header-simulator.h?rev=151737&r1=151736&r2=151737&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/system-header-simulator.h (original)
+++ cfe/trunk/test/Analysis/system-header-simulator.h Wed Feb 29 12:42:47 2012
@@ -2,16 +2,37 @@
 
 typedef struct _FILE FILE;
 extern FILE *stdin;
-int fscanf(FILE *restrict stream, const char *restrict format, ...);
+extern FILE *stdout;
+extern FILE *stderr;
+// Include a variant of standard streams that occur in the pre-processed file.
+extern FILE *__stdinp;
+extern FILE *__stdoutp;
+extern FILE *__stderrp;
+
+
+int fscanf(FILE *restrict, const char *restrict, ...);
 
 // Note, on some platforms errno macro gets replaced with a function call.
 extern int errno;
 
 unsigned long strlen(const char *);
 
-char *strcpy(char *restrict s1, const char *restrict s2);
+char *strcpy(char *restrict, const char *restrict);
 
 typedef unsigned long __darwin_pthread_key_t;
 typedef __darwin_pthread_key_t pthread_key_t;
-int pthread_setspecific(pthread_key_t ,
-         const void *);
+int pthread_setspecific(pthread_key_t, const void *);
+
+typedef long long __int64_t;
+typedef __int64_t __darwin_off_t;
+typedef __darwin_off_t fpos_t;
+
+void setbuf(FILE * restrict, char * restrict);
+int setvbuf(FILE * restrict, char * restrict, int, size_t);
+
+FILE *funopen(const void *,
+                 int (*)(void *, char *, int),
+                 int (*)(void *, const char *, int),
+                 fpos_t (*)(void *, fpos_t, int),
+                 int (*)(void *));
+





More information about the cfe-commits mailing list