Commit de409b6d authored by Jordan Rose's avatar Jordan Rose
Browse files

[analyzer] Buffers passed to CGBitmapContextCreate can escape.

Specifically, although the bitmap context does not take ownership of the
buffer (unlike CGBitmapContextCreateWithData), the data buffer can be extracted
out of the created CGContextRef. Thus the buffer is not leaked even if its
original pointer goes out of scope, as long as
- the context escapes, or
- it is retrieved via CGBitmapContextGetData and freed.

Actually implementing that logic is beyond the current scope of MallocChecker,
so for now CGBitmapContextCreate goes on our system function exception list.

llvm-svn: 158579
parent 13ffdd86
Loading
Loading
Loading
Loading
+7 −5
Original line number Original line Diff line number Diff line
@@ -1334,12 +1334,14 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
                return false;
                return false;
    }
    }


    // A bunch of other functions, which take ownership of a pointer (See retain
    // A bunch of other functions which either take ownership of a pointer or
    // release checker). Not all the parameters here are invalidated, but the
    // wrap the result up in a struct or object, meaning it can be freed later.
    // Malloc checker cannot differentiate between them. The right way of doing
    // (See RetainCountChecker.) Not all the parameters here are invalidated,
    // this would be to implement a pointer escapes callback.
    // but the Malloc checker cannot differentiate between them. The right way
    if (FName == "CVPixelBufferCreateWithBytes" ||
    // of doing this would be to implement a pointer escapes callback.
    if (FName == "CGBitmapContextCreate" ||
        FName == "CGBitmapContextCreateWithData" ||
        FName == "CGBitmapContextCreateWithData" ||
        FName == "CVPixelBufferCreateWithBytes" ||
        FName == "CVPixelBufferCreateWithPlanarBytes" ||
        FName == "CVPixelBufferCreateWithPlanarBytes" ||
        FName == "OSAtomicEnqueue") {
        FName == "OSAtomicEnqueue") {
      return false;
      return false;
+19 −0
Original line number Original line Diff line number Diff line
@@ -955,3 +955,22 @@ void test_double_assign_ints_positive()
  (void*)(long)(unsigned long)ptr; // expected-warning {{unused}} expected-warning {{leak}}
  (void*)(long)(unsigned long)ptr; // expected-warning {{unused}} expected-warning {{leak}}
}
}



void testCGContextNoLeak()
{
  void *ptr = malloc(16);
  CGContextRef context = CGBitmapContextCreate(ptr);

  // Because you can get the data back out like this, even much later,
  // CGBitmapContextCreate is one of our "stop-tracking" exceptions.
  free(CGBitmapContextGetData(context));
}

void testCGContextLeak()
{
  void *ptr = malloc(16);
  CGContextRef context = CGBitmapContextCreate(ptr);
  // However, this time we're just leaking the data, because the context
  // object doesn't escape and it hasn't been freed in this function.
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -46,3 +46,10 @@ typedef struct {


int dealocateMemWhenDoneByVal(void*, StWithCallback);
int dealocateMemWhenDoneByVal(void*, StWithCallback);
int dealocateMemWhenDoneByRef(StWithCallback*, const void*);
int dealocateMemWhenDoneByRef(StWithCallback*, const void*);

typedef struct CGContext *CGContextRef;
CGContextRef CGBitmapContextCreate(void *data/*, size_t width, size_t height,
                                   size_t bitsPerComponent, size_t bytesPerRow,
                                   CGColorSpaceRef space,
                                   CGBitmapInfo bitmapInfo*/);
void *CGBitmapContextGetData(CGContextRef context);