Сбой на устройстве iOS при разыменовании указателя, возвращенного decodeBytesForKey NSCoder - PullRequest
8 голосов
/ 11 октября 2011

Я обнаружил необычный сбой с NSCoder при использовании Apple LLVM Compiler 3.0 и скомпилированного с -O3.Вылетает только на устройствах.Я тестировал iPhone 4 под iOS 5, iPad 2 под iOS 5 и iPad 1 под iOS 4. Все происходит одинаково.Вот соответствующий раздел кода:

-(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;
}

А вот что такое BBPointI32:

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

EXC_BAD_ACCESS происходит, когда data разыменовывается.Это не проблема с нулевым указателем.Если я присоединяю GDB, я вижу, что длина равна 8, sizeof (BBPointI) также равен 8, и данные верны.

Если я посмотрю на разборку, произойдет сбой:

ldrd    r2, r3, [r0]

Что выглядит хорошо.r0 содержит 0xb546e, который является адресом data.Когда я проверяю эту память, я вижу, что она содержит ожидаемые данные.Для всех, кто интересуется, r2 содержит 72 (не уверен, что это такое), а r3 содержит 8 (наиболее вероятно, значение length).

Может кто-нибудь пролить свет на эту проблему?

Ответы [ 4 ]

10 голосов
/ 11 октября 2011

ldrd необходимо, чтобы адрес был выровнен по 8 байтов. Идиома данных * (BBPointI32 *) небезопасна, поскольку данные не выровнены по 8 байтов. Используйте memcpy, чтобы вместо этого получить байты в структуре.

4 голосов
/ 11 октября 2011

Вы используете ARC?Если так, то я считаю, что проблема в том, что компилятор может освободить decoder после вызова decodeBytesForKey: (следовательно, освобождает буфер, на который указывает возвращаемое значение).сборщик мусора имеет указатель .Вы можете CFRetain / CFRelease вашего декодера продлить срок его службы или просто добавить [decoder self] позже в методе, чтобы он оставался в живых до этой точки.решить эту проблему, пометив decoder с помощью __attribute__((objc_precise_lifetime)), но мое понимание этого атрибута несколько туманно.

3 голосов
/ 11 октября 2011

Ваш пример оставляет много переменных для опроса любым потенциальным помощником.Например: что, если с этим unarchiver есть что-то прикольное?Правильно ли управляется память?

Мне удалось воспроизвести сбой, который вы видите, и я могу подтвердить, что это происходит только при включенном параметре -O3, а не при выборе, например, Нет, для оптимизации.Здесь приведено сокращение кода сбоя, который устраняет внешние переменные, такие как управление памятью кодеров и т. Д. Обратите внимание, что в приведенном ниже коде целенаправленно сохраняются все объекты, чтобы исключить вероятность того, что сбой связан со случайным перепуском или побочнымЭффект от использования ARC, как было предложено Энди в другом ответе:

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];
}

Я думаю, что это дает более краткое описание проблемы, которую любой, кто хочет помочь, может использовать в качестве основы для погружения. Моя догадкадо сих пор это ошибка оптимизации в LLVM 3.0, но, возможно, у кого-то еще будет лучшая теория о том, что происходит.

Точка, которую вы не упомянули в своем вопросе, но которую я заметил всбой на моем устройстве, это сбой, сопровождаемый упоминанием ошибки EXC_ARM_DA_ALIGN в качестве причины исключения плохого доступа.Я гуглил пост в блоге, который, похоже, ссылается на те же симптомы и, вероятно, является причиной сбоя, как вы видите здесь:

http://www.galloway.me.uk/2010/10/arm-hacking-exc_arm_da_align-exception/

Действительно, изменив строку выше

decodedStruct = *(BBPointI32*)data;

до

memcpy(&decodedStruct, data, sizeof(decodedStruct));

Кажется, что поведение при сбое смягчено, и код ведет себя как ожидалось.

1 голос
/ 22 ноября 2012

Я попал в эту ветку, погуглив "EXC_ARM_DA_ALIGN" и "EXC_BAD_ACCESS".Ни один из других ответов не помог мне, так как эта ошибка возникла из-за чего-то относительно простого.Я написал:

theArray = [[NSArray alloc] initWithObjects:@"first", @"second", @"third",
                        @"fourth", @"fifth", "sixth", nil];

т.е. я остановил @ перед строковым литералом.Поместив его обратно, я решил ошибку.

...