diff options
Diffstat (limited to 'test/Analysis/malloc.c')
-rw-r--r-- | test/Analysis/malloc.c | 722 |
1 files changed, 634 insertions, 88 deletions
diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index d9087ab..3b47123 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -1,42 +1,33 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,experimental.core.CastSize,experimental.unix.Malloc -analyzer-store=region -verify %s +// 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 __attribute((ownership_returns(malloc))) *my_malloc(size_t); -void __attribute((ownership_takes(malloc, 1))) my_free(void *); -void __attribute((ownership_returns(malloc, 1))) *my_malloc2(size_t); -void __attribute((ownership_holds(malloc, 1))) my_hold(void *); - -// Duplicate attributes are silly, but not an error. -// Duplicate attribute has no extra effect. -// If two are of different kinds, that is an error and reported as such. -void __attribute((ownership_holds(malloc, 1))) -__attribute((ownership_holds(malloc, 1))) -__attribute((ownership_holds(malloc, 3))) my_hold2(void *, void *, void *); -void *my_malloc3(size_t); -void *myglobalpointer; -struct stuff { - void *somefield; -}; -struct stuff myglobalstuff; + +void myfoo(int *p); +void myfooint(int p); +char *fooRetPtr(); void f1() { int *p = malloc(12); - return; // expected-warning{{Allocated memory never released. Potential memory leak.}} + return; // expected-warning{{Memory is never released; potential leak}} } void f2() { int *p = malloc(12); free(p); - free(p); // expected-warning{{Try to free a memory block that has been released}} + 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{{Try to free a memory block that has been released}} + realloc(p,0); // expected-warning{{Attempt to free released memory}} } void f2_realloc_1() { @@ -44,105 +35,155 @@ void f2_realloc_1() { int *q = realloc(p,0); // no-warning } -// ownership attributes tests -void naf1() { - int *p = my_malloc3(12); - return; // 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}} + } } -void n2af1() { - int *p = my_malloc2(12); - return; // expected-warning{{Allocated memory never released. Potential memory leak.}} +int *realloctest1() { + int *q = malloc(12); + q = realloc(q, 20); + return q; // no warning - returning the allocated value } -void af1() { - int *p = my_malloc(12); - return; // expected-warning{{Allocated memory never released. Potential memory leak.}} +// 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 af1_b() { - int *p = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}} +void reallocSizeZero1() { + char *p = malloc(12); + char *r = realloc(p, 0); + if (!r) { + free(p); + } else { + free(r); + } } -void af1_c() { - myglobalpointer = my_malloc(12); // no-warning +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 af1_d() { - struct stuff mystuff; - mystuff.somefield = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}} +void reallocSizeZero3() { + char *p = malloc(12); + char *r = realloc(p, 0); + free(r); } -// Test that we can pass out allocated memory via pointer-to-pointer. -void af1_e(void **pp) { - *pp = my_malloc(42); // no-warning +void reallocSizeZero4() { + char *r = realloc(0, 0); + free(r); } -void af1_f(struct stuff *somestuff) { - somestuff->somefield = my_malloc(12); // no-warning +void reallocSizeZero5() { + char *r = realloc(0, 0); } -// Allocating memory for a field via multiple indirections to our arguments is OK. -void af1_g(struct stuff **pps) { - *pps = my_malloc(sizeof(struct stuff)); // no-warning - (*pps)->somefield = my_malloc(42); // no-warning +void reallocPtrZero1() { + char *r = realloc(0, 12); // expected-warning {{Memory is never released; potential leak}} } -void af2() { - int *p = my_malloc(12); - my_free(p); - free(p); // expected-warning{{Try to free a memory block that has been released}} +void reallocPtrZero2() { + char *r = realloc(0, 12); + if (r) + free(r); } -void af2b() { - int *p = my_malloc(12); - free(p); - my_free(p); // expected-warning{{Try to free a memory block that has been released}} +void reallocPtrZero3() { + char *r = realloc(0, 12); + free(r); } -void af2c() { - int *p = my_malloc(12); - free(p); - my_hold(p); // expected-warning{{Try to free a memory block that has been released}} +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 af2d() { - int *p = my_malloc(12); - free(p); - my_hold2(0, 0, p); // expected-warning{{Try to free a memory block that has been released}} +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); + } } -// No leak if malloc returns null. -void af2e() { - int *p = my_malloc(12); - if (!p) - return; // no-warning - free(p); // no-warning +void reallocRadar6337483_3() { + char * buf = malloc(100); + char * tmp; + tmp = (char*)realloc(buf, 0x1000000); + if (!tmp) { + free(buf); + return; + } + buf = tmp; + free(buf); } -// This case would inflict a double-free elsewhere. -// However, this case is considered an analyzer bug since it causes false-positives. -void af3() { - int *p = my_malloc(12); - my_hold(p); - free(p); // no-warning +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); + } } -// This case would inflict a double-free elsewhere. -// However, this case is considered an analyzer bug since it causes false-positives. -int * af4() { - int *p = my_malloc(12); - my_free(p); - return p; // no-warning +int *reallocfTest1() { + int *q = malloc(12); + q = reallocf(q, 20); + return q; // no warning - returning the allocated value } -// This case is (possibly) ok, be conservative -int * af5() { - int *p = my_malloc(12); - my_hold(p); - return p; // no-warning +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 @@ -199,13 +240,13 @@ void pr6293() { void f7() { char *x = (char*) malloc(4); free(x); - x[0] = 'a'; // expected-warning{{Use dynamically allocated memory after it is freed.}} + 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 dynamically allocated memory after it is freed.}} + x[0] = 'a'; // expected-warning{{Use of memory after it is freed}} } void PR6123() { @@ -261,3 +302,508 @@ char callocZeroesBad () { } 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; +} + |