summaryrefslogtreecommitdiff
path: root/clang/test/Analysis/malloc.c
diff options
context:
space:
mode:
authorCarlo Zancanaro <carlo@pc-4w14-0.cs.usyd.edu.au>2012-10-15 17:10:06 +1100
committerCarlo Zancanaro <carlo@pc-4w14-0.cs.usyd.edu.au>2012-10-15 17:10:06 +1100
commitbe1de4be954c80875ad4108e0a33e8e131b2f2c0 (patch)
tree1fbbecf276bf7c7bdcbb4dd446099d6d90eaa516 /clang/test/Analysis/malloc.c
parentc4626a62754862d20b41e8a46a3574264ea80e6d (diff)
parentf1bd2e48c5324d3f7cda4090c87f8a5b6f463ce2 (diff)
Merge branch 'master' of ssh://bitbucket.org/czan/honours
Diffstat (limited to 'clang/test/Analysis/malloc.c')
-rw-r--r--clang/test/Analysis/malloc.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c
new file mode 100644
index 0000000..3b47123
--- /dev/null
+++ b/clang/test/Analysis/malloc.c
@@ -0,0 +1,809 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,experimental.core.CastSize,unix.Malloc -analyzer-store=region -verify %s
+#include "system-header-simulator.h"
+
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void *valloc(size_t);
+void free(void *);
+void *realloc(void *ptr, size_t size);
+void *reallocf(void *ptr, size_t size);
+void *calloc(size_t nmemb, size_t size);
+
+void myfoo(int *p);
+void myfooint(int p);
+char *fooRetPtr();
+
+void f1() {
+ int *p = malloc(12);
+ return; // expected-warning{{Memory is never released; potential leak}}
+}
+
+void f2() {
+ int *p = malloc(12);
+ free(p);
+ free(p); // expected-warning{{Attempt to free released memory}}
+}
+
+void f2_realloc_0() {
+ int *p = malloc(12);
+ realloc(p,0);
+ realloc(p,0); // expected-warning{{Attempt to free released memory}}
+}
+
+void f2_realloc_1() {
+ int *p = malloc(12);
+ int *q = realloc(p,0); // no-warning
+}
+
+void reallocNotNullPtr(unsigned sizeIn) {
+ unsigned size = 12;
+ char *p = (char*)malloc(size);
+ if (p) {
+ char *q = (char*)realloc(p, sizeIn);
+ char x = *q; // expected-warning {{Memory is never released; potential leak}}
+ }
+}
+
+int *realloctest1() {
+ int *q = malloc(12);
+ q = realloc(q, 20);
+ return q; // no warning - returning the allocated value
+}
+
+// p should be freed if realloc fails.
+void reallocFails() {
+ char *p = malloc(12);
+ char *r = realloc(p, 12+1);
+ if (!r) {
+ free(p);
+ } else {
+ free(r);
+ }
+}
+
+void reallocSizeZero1() {
+ char *p = malloc(12);
+ char *r = realloc(p, 0);
+ if (!r) {
+ free(p);
+ } else {
+ free(r);
+ }
+}
+
+void reallocSizeZero2() {
+ char *p = malloc(12);
+ char *r = realloc(p, 0);
+ if (!r) {
+ free(p);
+ } else {
+ free(r);
+ }
+ free(p); // expected-warning {{Attempt to free released memory}}
+}
+
+void reallocSizeZero3() {
+ char *p = malloc(12);
+ char *r = realloc(p, 0);
+ free(r);
+}
+
+void reallocSizeZero4() {
+ char *r = realloc(0, 0);
+ free(r);
+}
+
+void reallocSizeZero5() {
+ char *r = realloc(0, 0);
+}
+
+void reallocPtrZero1() {
+ char *r = realloc(0, 12); // expected-warning {{Memory is never released; potential leak}}
+}
+
+void reallocPtrZero2() {
+ char *r = realloc(0, 12);
+ if (r)
+ free(r);
+}
+
+void reallocPtrZero3() {
+ char *r = realloc(0, 12);
+ free(r);
+}
+
+void reallocRadar6337483_1() {
+ char *buf = malloc(100);
+ buf = (char*)realloc(buf, 0x1000000);
+ if (!buf) {
+ return;// expected-warning {{Memory is never released; potential leak}}
+ }
+ free(buf);
+}
+
+void reallocRadar6337483_2() {
+ char *buf = malloc(100);
+ char *buf2 = (char*)realloc(buf, 0x1000000);
+ if (!buf2) { // expected-warning {{Memory is never released; potential leak}}
+ ;
+ } else {
+ free(buf2);
+ }
+}
+
+void reallocRadar6337483_3() {
+ char * buf = malloc(100);
+ char * tmp;
+ tmp = (char*)realloc(buf, 0x1000000);
+ if (!tmp) {
+ free(buf);
+ return;
+ }
+ buf = tmp;
+ free(buf);
+}
+
+void reallocRadar6337483_4() {
+ char *buf = malloc(100);
+ char *buf2 = (char*)realloc(buf, 0x1000000);
+ if (!buf2) {
+ return; // expected-warning {{Memory is never released; potential leak}}
+ } else {
+ free(buf2);
+ }
+}
+
+int *reallocfTest1() {
+ int *q = malloc(12);
+ q = reallocf(q, 20);
+ return q; // no warning - returning the allocated value
+}
+
+void reallocfRadar6337483_4() {
+ char *buf = malloc(100);
+ char *buf2 = (char*)reallocf(buf, 0x1000000);
+ if (!buf2) {
+ return; // no warning - reallocf frees even on failure
+ } else {
+ free(buf2);
+ }
+}
+
+void reallocfRadar6337483_3() {
+ char * buf = malloc(100);
+ char * tmp;
+ tmp = (char*)reallocf(buf, 0x1000000);
+ if (!tmp) {
+ free(buf); // expected-warning {{Attempt to free released memory}}
+ return;
+ }
+ buf = tmp;
+ free(buf);
+}
+
+void reallocfPtrZero1() {
+ char *r = reallocf(0, 12); // expected-warning {{Memory is never released; potential leak}}
+}
+
+
+// This case tests that storing malloc'ed memory to a static variable which is
+// then returned is not leaked. In the absence of known contracts for functions
+// or inter-procedural analysis, this is a conservative answer.
+int *f3() {
+ static int *p = 0;
+ p = malloc(12);
+ return p; // no-warning
+}
+
+// This case tests that storing malloc'ed memory to a static global variable
+// which is then returned is not leaked. In the absence of known contracts for
+// functions or inter-procedural analysis, this is a conservative answer.
+static int *p_f4 = 0;
+int *f4() {
+ p_f4 = malloc(12);
+ return p_f4; // no-warning
+}
+
+int *f5() {
+ int *q = malloc(12);
+ q = realloc(q, 20);
+ return q; // no-warning
+}
+
+void f6() {
+ int *p = malloc(12);
+ if (!p)
+ return; // no-warning
+ else
+ free(p);
+}
+
+void f6_realloc() {
+ int *p = malloc(12);
+ if (!p)
+ return; // no-warning
+ else
+ realloc(p,0);
+}
+
+
+char *doit2();
+void pr6069() {
+ char *buf = doit2();
+ free(buf);
+}
+
+void pr6293() {
+ free(0);
+}
+
+void f7() {
+ char *x = (char*) malloc(4);
+ free(x);
+ x[0] = 'a'; // expected-warning{{Use of memory after it is freed}}
+}
+
+void f7_realloc() {
+ char *x = (char*) malloc(4);
+ realloc(x,0);
+ x[0] = 'a'; // expected-warning{{Use of memory after it is freed}}
+}
+
+void PR6123() {
+ int *x = malloc(11); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}}
+}
+
+void PR7217() {
+ int *buf = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}}
+ buf[1] = 'c'; // not crash
+}
+
+void mallocCastToVoid() {
+ void *p = malloc(2);
+ const void *cp = p; // not crash
+ free(p);
+}
+
+void mallocCastToFP() {
+ void *p = malloc(2);
+ void (*fp)() = p; // not crash
+ free(p);
+}
+
+// This tests that malloc() buffers are undefined by default
+char mallocGarbage () {
+ char *buf = malloc(2);
+ char result = buf[1]; // expected-warning{{undefined}}
+ free(buf);
+ return result;
+}
+
+// This tests that calloc() buffers need to be freed
+void callocNoFree () {
+ char *buf = calloc(2,2);
+ return; // expected-warning{{never released}}
+}
+
+// These test that calloc() buffers are zeroed by default
+char callocZeroesGood () {
+ char *buf = calloc(2,2);
+ char result = buf[3]; // no-warning
+ if (buf[1] == 0) {
+ free(buf);
+ }
+ return result; // no-warning
+}
+
+char callocZeroesBad () {
+ char *buf = calloc(2,2);
+ char result = buf[3]; // no-warning
+ if (buf[1] != 0) {
+ free(buf); // expected-warning{{never executed}}
+ }
+ return result; // expected-warning{{never released}}
+}
+
+void nullFree() {
+ int *p = 0;
+ free(p); // no warning - a nop
+}
+
+void paramFree(int *p) {
+ myfoo(p);
+ free(p); // no warning
+ myfoo(p); // TODO: This should be a warning.
+}
+
+int* mallocEscapeRet() {
+ int *p = malloc(12);
+ return p; // no warning
+}
+
+void mallocEscapeFoo() {
+ int *p = malloc(12);
+ myfoo(p);
+ return; // no warning
+}
+
+void mallocEscapeFree() {
+ int *p = malloc(12);
+ myfoo(p);
+ free(p);
+}
+
+void mallocEscapeFreeFree() {
+ int *p = malloc(12);
+ myfoo(p);
+ free(p);
+ free(p); // expected-warning{{Attempt to free released memory}}
+}
+
+void mallocEscapeFreeUse() {
+ int *p = malloc(12);
+ myfoo(p);
+ free(p);
+ myfoo(p); // expected-warning{{Use of memory after it is freed}}
+}
+
+int *myalloc();
+void myalloc2(int **p);
+
+void mallocEscapeFreeCustomAlloc() {
+ int *p = malloc(12);
+ myfoo(p);
+ free(p);
+ p = myalloc();
+ free(p); // no warning
+}
+
+void mallocEscapeFreeCustomAlloc2() {
+ int *p = malloc(12);
+ myfoo(p);
+ free(p);
+ myalloc2(&p);
+ free(p); // no warning
+}
+
+void mallocBindFreeUse() {
+ int *x = malloc(12);
+ int *y = x;
+ free(y);
+ myfoo(x); // expected-warning{{Use of memory after it is freed}}
+}
+
+void mallocEscapeMalloc() {
+ int *p = malloc(12);
+ myfoo(p);
+ p = malloc(12); // expected-warning{{Memory is never released; potential leak}}
+}
+
+void mallocMalloc() {
+ int *p = malloc(12);
+ p = malloc(12); // expected-warning 2 {{Memory is never released; potential leak}}
+}
+
+void mallocFreeMalloc() {
+ int *p = malloc(12);
+ free(p);
+ p = malloc(12);
+ free(p);
+}
+
+void mallocFreeUse_params() {
+ int *p = malloc(12);
+ free(p);
+ myfoo(p); //expected-warning{{Use of memory after it is freed}}
+}
+
+void mallocFreeUse_params2() {
+ int *p = malloc(12);
+ free(p);
+ myfooint(*p); //expected-warning{{Use of memory after it is freed}}
+}
+
+void mallocFailedOrNot() {
+ int *p = malloc(12);
+ if (!p)
+ free(p);
+ else
+ free(p);
+}
+
+struct StructWithInt {
+ int g;
+};
+
+int *mallocReturnFreed() {
+ int *p = malloc(12);
+ free(p);
+ return p; // expected-warning {{Use of memory after it is freed}}
+}
+
+int useAfterFreeStruct() {
+ struct StructWithInt *px= malloc(sizeof(struct StructWithInt));
+ px->g = 5;
+ free(px);
+ return px->g; // expected-warning {{Use of memory after it is freed}}
+}
+
+void nonSymbolAsFirstArg(int *pp, struct StructWithInt *p);
+
+void mallocEscapeFooNonSymbolArg() {
+ struct StructWithInt *p = malloc(sizeof(struct StructWithInt));
+ nonSymbolAsFirstArg(&p->g, p);
+ return; // no warning
+}
+
+void mallocFailedOrNotLeak() {
+ int *p = malloc(12);
+ if (p == 0)
+ return; // no warning
+ else
+ return; // expected-warning {{Memory is never released; potential leak}}
+}
+
+void mallocAssignment() {
+ char *p = malloc(12);
+ p = fooRetPtr(); // expected-warning {{leak}}
+}
+
+int vallocTest() {
+ char *mem = valloc(12);
+ return 0; // expected-warning {{Memory is never released; potential leak}}
+}
+
+void vallocEscapeFreeUse() {
+ int *p = valloc(12);
+ myfoo(p);
+ free(p);
+ myfoo(p); // expected-warning{{Use of memory after it is freed}}
+}
+
+int *Gl;
+struct GlStTy {
+ int *x;
+};
+
+struct GlStTy GlS = {0};
+
+void GlobalFree() {
+ free(Gl);
+}
+
+void GlobalMalloc() {
+ Gl = malloc(12);
+}
+
+void GlobalStructMalloc() {
+ int *a = malloc(12);
+ GlS.x = a;
+}
+
+void GlobalStructMallocFree() {
+ int *a = malloc(12);
+ GlS.x = a;
+ free(GlS.x);
+}
+
+char *ArrayG[12];
+
+void globalArrayTest() {
+ char *p = (char*)malloc(12);
+ ArrayG[0] = p;
+}
+
+// Make sure that we properly handle a pointer stored into a local struct/array.
+typedef struct _StructWithPtr {
+ int *memP;
+} StructWithPtr;
+
+static StructWithPtr arrOfStructs[10];
+
+void testMalloc() {
+ int *x = malloc(12);
+ StructWithPtr St;
+ St.memP = x;
+ arrOfStructs[0] = St;
+}
+
+StructWithPtr testMalloc2() {
+ int *x = malloc(12);
+ StructWithPtr St;
+ St.memP = x;
+ return St;
+}
+
+int *testMalloc3() {
+ int *x = malloc(12);
+ int *y = x;
+ return y;
+}
+
+void testElemRegion1() {
+ char *x = (void*)malloc(2);
+ int *ix = (int*)x;
+ free(&(x[0]));
+}
+
+void testElemRegion2(int **pp) {
+ int *p = malloc(12);
+ *pp = p;
+ free(pp[0]);
+}
+
+void testElemRegion3(int **pp) {
+ int *p = malloc(12);
+ *pp = p;
+ free(*pp);
+}
+// Region escape testing.
+
+unsigned takePtrToPtr(int **p);
+void PassTheAddrOfAllocatedData(int f) {
+ int *p = malloc(12);
+ // We don't know what happens after the call. Should stop tracking here.
+ if (takePtrToPtr(&p))
+ f++;
+ free(p); // no warning
+}
+
+struct X {
+ int *p;
+};
+unsigned takePtrToStruct(struct X *s);
+int ** foo2(int *g, int f) {
+ int *p = malloc(12);
+ struct X *px= malloc(sizeof(struct X));
+ px->p = p;
+ // We don't know what happens after this call. Should not track px nor p.
+ if (takePtrToStruct(px))
+ f++;
+ free(p);
+ return 0;
+}
+
+struct X* RegInvalidationDetect1(struct X *s2) {
+ struct X *px= malloc(sizeof(struct X));
+ px->p = 0;
+ px = s2;
+ return px; // expected-warning {{Memory is never released; potential leak}}
+}
+
+struct X* RegInvalidationGiveUp1() {
+ int *p = malloc(12);
+ struct X *px= malloc(sizeof(struct X));
+ px->p = p;
+ return px;
+}
+
+int **RegInvalidationDetect2(int **pp) {
+ int *p = malloc(12);
+ pp = &p;
+ pp++;
+ return 0;// expected-warning {{Memory is never released; potential leak}}
+}
+
+extern void exit(int) __attribute__ ((__noreturn__));
+void mallocExit(int *g) {
+ struct xx *p = malloc(12);
+ if (g != 0)
+ exit(1);
+ free(p);
+ return;
+}
+
+extern void __assert_fail (__const char *__assertion, __const char *__file,
+ unsigned int __line, __const char *__function)
+ __attribute__ ((__noreturn__));
+#define assert(expr) \
+ ((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__))
+void mallocAssert(int *g) {
+ struct xx *p = malloc(12);
+
+ assert(g != 0);
+ free(p);
+ return;
+}
+
+void doNotInvalidateWhenPassedToSystemCalls(char *s) {
+ char *p = malloc(12);
+ strlen(p);
+ strcpy(p, s); // expected-warning {{leak}}
+}
+
+// Rely on the CString checker evaluation of the strcpy API to convey that the result of strcpy is equal to p.
+void symbolLostWithStrcpy(char *s) {
+ char *p = malloc(12);
+ p = strcpy(p, s);
+ free(p);
+}
+
+
+// The same test as the one above, but with what is actually generated on a mac.
+static __inline char *
+__inline_strcpy_chk (char *restrict __dest, const char *restrict __src)
+{
+ return __builtin___strcpy_chk (__dest, __src, __builtin_object_size (__dest, 2 > 1));
+}
+
+void symbolLostWithStrcpy_InlineStrcpyVersion(char *s) {
+ char *p = malloc(12);
+ p = ((__builtin_object_size (p, 0) != (size_t) -1) ? __builtin___strcpy_chk (p, s, __builtin_object_size (p, 2 > 1)) : __inline_strcpy_chk (p, s));
+ free(p);
+}
+
+// Here we are returning a pointer one past the allocated value. An idiom which
+// can be used for implementing special malloc. The correct uses of this might
+// be rare enough so that we could keep this as a warning.
+static void *specialMalloc(int n){
+ int *p;
+ p = malloc( n+8 );
+ if( p ){
+ p[0] = n;
+ p++;
+ }
+ return p;
+}
+
+// Potentially, the user could free the struct by performing pointer arithmetic on the return value.
+// This is a variation of the specialMalloc issue, though probably would be more rare in correct code.
+int *specialMallocWithStruct() {
+ struct StructWithInt *px= malloc(sizeof(struct StructWithInt));
+ return &(px->g);
+}
+
+// Test various allocation/deallocation functions.
+
+char *strdup(const char *s);
+char *strndup(const char *s, size_t n);
+
+void testStrdup(const char *s, unsigned validIndex) {
+ char *s2 = strdup(s);
+ s2[validIndex + 1] = 'b';// expected-warning {{Memory is never released; potential leak}}
+}
+
+int testStrndup(const char *s, unsigned validIndex, unsigned size) {
+ char *s2 = strndup(s, size);
+ s2 [validIndex + 1] = 'b';
+ if (s2[validIndex] != 'a')
+ return 0;
+ else
+ return 1;// expected-warning {{Memory is never released; potential leak}}
+}
+
+void testStrdupContentIsDefined(const char *s, unsigned validIndex) {
+ char *s2 = strdup(s);
+ char result = s2[1];// no warning
+ 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
+// binding must be freed; otherwise, a memory leak can occur.
+void testPthereadSpecificEscape(pthread_key_t key) {
+ void *buf = malloc(12);
+ 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}}
+}
+
+// <rdar://problem/10978247>.
+// some people use stack allocated memory as an optimization to avoid
+// a heap allocation for small work sizes. This tests the analyzer's
+// understanding that the malloc'ed memory is not the same as stackBuffer.
+void radar10978247(int myValueSize) {
+ char stackBuffer[128];
+ char *buffer;
+
+ if (myValueSize <= sizeof(stackBuffer))
+ buffer = stackBuffer;
+ else
+ buffer = malloc(myValueSize);
+
+ // do stuff with the buffer
+ if (buffer != stackBuffer)
+ free(buffer);
+}
+
+void radar10978247_positive(int myValueSize) {
+ char stackBuffer[128];
+ char *buffer;
+
+ if (myValueSize <= sizeof(stackBuffer))
+ buffer = stackBuffer;
+ else
+ buffer = malloc(myValueSize);
+
+ // do stuff with the buffer
+ if (buffer == stackBuffer) // expected-warning {{leak}}
+ return;
+}
+
+// ----------------------------------------------------------------------------
+// Below are the known false positives.
+
+// TODO: There should be no warning here. This one might be difficult to get rid of.
+void dependsOnValueOfPtr(int *g, unsigned f) {
+ int *p;
+
+ if (f) {
+ p = g;
+ } else {
+ p = malloc(12);
+ }
+
+ if (p != g)
+ free(p);
+ else
+ return; // expected-warning{{Memory is never released; potential leak}}
+ return;
+}
+
+// ----------------------------------------------------------------------------
+// False negatives.
+
+// TODO: This requires tracking symbols stored inside the structs/arrays.
+void testMalloc5() {
+ StructWithPtr St;
+ StructWithPtr *pSt = &St;
+ pSt->memP = malloc(12);
+}
+
+// TODO: This is another false negative.
+void testMallocWithParam(int **p) {
+ *p = (int*) malloc(sizeof(int));
+ *p = 0;
+}
+
+void testMallocWithParam_2(int **p) {
+ *p = (int*) malloc(sizeof(int));
+}
+
+// TODO: This should produce a warning, similar to the previous issue.
+void localArrayTest() {
+ char *p = (char*)malloc(12);
+ char *ArrayL[12];
+ ArrayL[0] = p;
+}
+