3

I have the following method:

+ (NSString*) getMD5HashFromFile:(NSString*)filePath {
    CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
    NSString *hashStr = (NSString*)md5hash;
    CFRelease(md5hash);
    return hashStr;
}

I was getting random crashes on the Simulator, about 1 in 20-30 executions. The fact that this wasn't consistent didn't help me dig deeper before.

Now that I see the code again, it seems obvious that md5hash gets released before being returned, which means the returned object is invalidated. The returned value is used in another method in a consistent way that crashes sometimes, but not always. My question is why this only happens rarely and not always.

Does it have something to do with the mix of Obj-C and C code and the way autorelease pools work?

Note: The bug seems to be fixed by using NSString *hashStr = [NSString stringWithString:(NSString*)md5hash], which makes total sense to me.

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
dimitrios
  • 469
  • 5
  • 15

3 Answers3

5

Just because a piece of memory is released and deallocated doesn't mean that it's immediately returned to the OS. Your application can hold onto it for an arbitrary period of time based on numerous factors and at several layers. The OS has more important things to do sometimes than reclaim every piece of memory you let go of and might ask for again in half a second. Accessing memory that you've called free() on, but technically own, does not generate a signal. This is why MallocScribble exists. It overwrites memory that you free with trash (0x55) so that it's more obvious when you use freed memory.

Try the following:

char *foo = malloc(100);
strcpy(foo, "stuff");
free(foo);
printf("%s", foo);

Most of the time that'll work fine, despite being completely wrong. Now, edit your Scheme>Diagnostics and Enable Scribble. Re-run and you'll see a bunch of "U" (0x55) indicating that you're reading nonsense. But it still won't crash.

You may be interested in Matt Gallagher's A look at how malloc works on the Mac for a bit more on the topic.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thank you Rob. I think this is a pretty good explanation. Actually, now that I see it writen, I unserstand that I already new this. Apparently not well enough :) Thanks again – dimitrios Mar 05 '12 at 19:17
  • We all forget stuff we think we should have remembered :D https://twitter.com/#!/cocoaphony/status/174989186032603136 – Rob Napier Mar 05 '12 at 20:40
2

CFRelease argument must not be NULL.

If CFRelease argument is NULL, this will cause a runtime error and your application will crash

if(md5hash)
CFRelease(md5hash);
Parag Bafna
  • 22,812
  • 8
  • 71
  • 144
0
+(NSString*) getMD5HashFromFile:(NSString*)filePath {
    CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
    NSString *hashStr = [(NSString*)md5hash copy];
    CFRelease(md5hash);
    return [hashStr autorelease];
}

make sure to retain the returned value in the caller if you need to hang on to it for any length of time.

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
Kirby Todd
  • 11,254
  • 3
  • 32
  • 60