• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive


  • Subject: Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive
  • From: Jerry Krinock <email@hidden>
  • Date: Thu, 2 Jul 2009 20:17:13 -0700

If -[NSKeyedUnarchiver unarchiveObjectWithData:] is handed a corrupt archive, it raises an exception AND crashes the program. I find this to be very annoying since, almost always, archives come from files or network sources where corruption is possible. Therefore, I have always protected this invocation in a try/catch block.

As I was about to do this for about the fifth time this year, I decided to use the Method Replacement feature of Leopard to replace this method, once and for all, with one that wouldn't crash. I understand that Method Replacement should not be done casually because any plug-in code and even Cocoa itself will use the replaced method. However, I can't think of any usage where a crash would be required behavior.

It seems to work fine, after 10 minutes of testing. Does anyone see any problem with this?

Sincerely,

Jerry Krinock


#import <Cocoa/Cocoa.h>


/*! @brief Improvements to NSKeyedUnarchiver

@details Method +unarchiveObjectWithData: has been replaced so that
instead of raising an exception and crashing if given a corrupt archive,
it just returns nil. Also, another method has been added which
returns the exception.
*/
@interface NSKeyedUnarchiver (CatchExceptions)


/*!
 @brief    Like unarchiveObjectWithData:, except it returns the
 exception by reference.

 @param    exception_p  Pointer which will, upon return, if an
 exception occurred and said pointer is not NULL, point to said
 NSException.
 */
+ (id)unarchiveObjectWithData:(NSData*)data
                  exception_p:(NSException**)exception_p ;

@end

#import "NSKeyedUnarchiver+CatchExceptions.h"
#import <objc/runtime.h>

@implementation NSKeyedUnarchiver (CatchExceptions)

+ (id)unarchiveObjectWithData:(NSData*)data
                  exception_p:(NSException**)exception_p {
    id object = nil ;

@try {
// Note: Since methods were swapped, this is invoking the original method
object = [NSKeyedUnarchiver replacement_unarchiveObjectWithData:data] ;
}
@catch (NSException* exception) {
if (exception_p) {
*exception_p = exception ;
}
}
@finally{
}


    return object ;
}

+ (void)load {
// Swap the implementations of +unarchiveObjectWithData: and +replacement_unarchiveObjectWithData:.
// When the +unarchiveObjectWithData: message is sent to the NSKeyedUnarchiver class object,
// +replacement_unarchiveObjectWithData: will be invoked instead. Conversely,
// +replacement_unarchiveObjectWithData: will invoke +unarchiveObjectWithData:.
Method originalMethod = class_getClassMethod(self, @selector(unarchiveObjectWithData:)) ;
Method replacedMethod = class_getClassMethod(self, @selector(replacement_unarchiveObjectWithData:)) ;
method_exchangeImplementations(originalMethod, replacedMethod) ;
}


+ (id)replacement_unarchiveObjectWithData:(NSData*)data {
    return [self unarchiveObjectWithData:data
                             exception_p:NULL] ;
}

@end


TEST CODE:

int main (int argc, const char * argv[]) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

// Try to unarchive your bash profile
// This ain't gonna be able to unarchive
NSData* data = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@".bash_profile"]] ;
NSLog(@"Your bash profile data is %d bytes.", [data length]) ;


    id whatever ;

    // Try it using the normal method
    whatever = [NSKeyedUnarchiver unarchiveObjectWithData:data] ;
    NSLog(@"1.  unarchived whatever = %@", whatever) ;

    // Try it using the improved method that returns the exception:
    NSException** exception ;
    whatever = [NSKeyedUnarchiver unarchiveObjectWithData:data
                                              exception_p:&exception] ;
    NSLog(@"2.  unarchived whatever = %@", whatever) ;
    NSLog(@"exception = %@", exception) ;

    [pool release] ;
}



_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive
      • From: Jim Correia <email@hidden>
    • Re: Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive
      • From: Michael Ash <email@hidden>
  • Prev by Date: Re: Sorting on a table view of a managed object - debugger throws exception
  • Next by Date: Re: Windows on main thread always only sometimes?
  • Previous by thread: Re: Possible NSPredicateEditor bug (found workaround)
  • Next by thread: Re: Replace -[NSKeyedUnarchiver unarchiveObjectWithData:] so it doesn't crash on corrupt archive
  • Index(es):
    • Date
    • Thread