[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