My application reads and resizes images that are loaded from the internet; and unfortunately I can't control the creation of these images.
Recently I had a crash that I am not sure how best to be able to handle. In this case the image was a corrupt GIF file. It wasn't badly corrupted but it was reporting a resolution size (height x width) that wasn't accurate. The image was supposed to be a 400x600 image but was reporting something like 1111x999.
The code snippet that crashed is:
- (void) saveRawImageRefToDisk:(CGImageRef)image withUUID:(NSString *)uuid andType:(NSString *)type {
if (!uuid || !type) {
return;
}
NSDictionary *imageDestinationWriteOptions = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1], (id)kCGImagePropertyOrientation,
(id)kCFBooleanFalse, (id)kCGImagePropertyHasAlpha,
[NSNumber numberWithInt:1.0], kCGImageDestinationLossyCompressionQuality,
nil];
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL((CFURLRef)[NSURL fileURLWithPath:[self getRawImagePathForUUID:uuid]], (CFStringRef)type, 1, nil);
if (imageDestination) {
CGImageDestinationAddImage(imageDestination, image, (CFDictionaryRef)imageDestinationWriteOptions);
CGImageDestinationFinalize(imageDestination); //<--- EXC_BAD_ACCESS in here
CFRelease(imageDestination);
}
}
The snippet of the crash log is:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x06980000
Crashed Thread: 8
Thread 8 name: Dispatch queue: com.apple.root.default-priority
Thread 8 Crashed:
0 libsystem_c.dylib 0x348cf6a4 memcpy$VARIANT$CortexA9 + 212
1 CoreGraphics 0x366de30c CGAccessSessionGetBytes + 112
2 ImageIO 0x32941b04 GenerateFromRGBImageWu + 256
3 ImageIO 0x329432aa _CGImagePluginWriteGIF + 3390
4 ImageIO 0x32932b3a CGImageDestinationFinalize + 118
The image in question was a GIF so I had the type in my method set to @"com.compuserve.gif". As far as I can tell the CGImageDestinationRef imageDestination was a valid object.
Part of the logic of my app is to keep images in the same format as they are read. So in this case I was resizing and writing back out a GIF.
I tried forcing to write out the CGImageRef as a PNG and interestingly the application didn't crash. It wrote out the PNG (which wasn't valid -- just a blank black image), but it didn't crash. But I've no idea in normal course of execution to know that I should write a GIF as a PNG.
Anybody got any ideas as to how I can handle or trap this condition? I'd greatly appreaciate any suggestions as this is a worry for me re: application stability. Or is this just a straight up "report as a radar to Apple"?
Edits & supplementary code:
All of this processing occurs within a NSOperationQueue and not on the main thread
Image in question is available at http://dribbble.com/system/users/1409/screenshots/182540/htg-logo-tiny.gif?1306878218
Here is the function where I create the CGImageRef:
- (CGImageRef) createIpadSizedImageRefFromImageSource:(CGImageSourceRef)imageSource withSourceSizeOf(CGSize)imageSourceSize {
NSDictionary *imageCreationOptions = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
[NSNumber numberWithInt:1024], kCGImageSourceThumbnailMaxPixelSize,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
nil];
CGImageRef image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (CFDictionaryRef)imageCreationOptions);
if (!image) {
return nil;
} else {
[(id) image autorelease];
return image;
}
}
and the CGImageSourceRef is created from a filepath like:
- (CGImageSourceRef) createImageSourceFromURL:(NSURL *)imageLocationOnDisk {
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageLocationOnDisk, NULL);
if (!imageSource) {
DebugLog(@"Issue creating image source for image at: %@", imageLocationOnDisk);
return nil;
} else {
[(id) imageSource autorelease];
return imageSource;
}
}