Alex Rivera | Logout

Crash on iOS device when dereferencing a pointer returned by NSCoder's decodeBytesForKey

Asked 2011-10-11T16:07:25.873
8

I've found an unusual crasher with NSCoder when using the Apple LLVM Compiler 3.0 and compiled with -O3. It only crashes on devices. I've tested an iPhone 4 running iOS 5, an iPad 2 running iOS 5 and an iPad 1 running iOS 4. All crash identically. Here's the relevant section of code:

-(id)initWithCoder:(NSCoder*)decoder
{
    if (![super init])
    {
        return nil;
    }

    NSUInteger length = 0;

    uint8_t* data = (uint8_t*)[decoder decodeBytesForKey:BBKey returnedLength:&length];

    m_value = *(BBPointI32*)data;

    return self;
}

And here's what a BBPointI32 is:

typedef struct
{
    NSInteger x;
    NSInteger y;
}
BBPointI32;

The EXC_BAD_ACCESS happens when data is dereferenced. This is not a null pointer issue. If I attach GDB, I can see that length is 8, sizeof(BBPointI) is also 8 and the data is correct.

If I look at the disassembly, the crash is happening on:

ldrd    r2, r3, [r0]

Which looks fine. r0 contains 0xb546e, which is the address of data. When I inspect that memory, I can see that it contains the data I expect. For anyone who's interested, r2 contains 72 (not sure what that is) and r3 contains 8 (most probably the value of length).

Can anyone shed some light on this issue?

Edit
Report

1 Answer

3

Your example leaves a lot of variables to be questioned by any potential helper. For example: what if there's something funky with this unarchiver? Is memory being managed correctly?

I was able to reproduce the crash you're seeing, and can confirm it only occurs when -O3 is enabled, and not when e.g. None is selected for optimization. Here is a reduction of the crashing code that eliminates outside variables such as memory management of the coders, etc. Note the code below purposefully retains all objects to eliminate the possibility that the crash is related to an accidental over-release, or a side-effect of using ARC, as suggested by Andy in another answer:

typedef struct
{
    NSInteger x;
    NSInteger y;
}
BBPointI32;

- (void) testDecoding
{
    NSString* myKey = @"Testing";

    // First get an coder with bytes in it
    NSMutableData* myData = [[NSMutableData data] retain];
    NSKeyedArchiver* myCoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:myData];

    BBPointI32 encodedStruct = {1234, 5678};
    [myCoder encodeBytes:(const uint8_t *)&encodedStruct length:sizeof(encodedStruct) forKey:myKey];
    [myCoder finishEncoding];

    // Now decode it
    BBPointI32 decodedStruct;
    NSUInteger decodedLength = 0;
    NSKeyedUnarchiver* myDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:myData];
    uint8_t* data = (uint8_t*)[myDecoder decodeBytesForKey:myKey returnedLength:&decodedLength];
    decodedStruct = *(BBPointI32*)data;
    NSLog(@"Got decoded struct with x = %ld, y = %ld, length = %lu", decodedStruct.x, decodedStruct.y, decodedLength);
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    NSLog(@"Testing decoding");
    [self testDecoding];
}

I think this gives a more succinct description of the problem that anybody who wants to help can use as a basis for diving in. My hunch thus far is this is an optimization bug in LLVM 3.0, but maybe somebody e

answered 2011-10-11T17:01:21.943

Your Answer