2011-10-11 10 views
8

Ho trovato un insolito crasher con NSCoder quando si utilizza Apple LLVM Compiler 3.0 e compilato con -O3. Si blocca solo sui dispositivi. Ho testato un iPhone 4 con iOS 5, un iPad 2 con iOS 5 e un iPad 1 con iOS 4. Tutto si blocca in modo identico. Ecco la relativa sezione di codice:Arresto anomalo del dispositivo iOS durante il dereferenziamento di un puntatore restituito dalla decodifica di NSCoderBytesForKey

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

Ed ecco quello che un BBPointI32 è:

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

Il EXC_BAD_ACCESS accade quando data è dereferenziato. Questo è non un problema di puntatore nullo. Se allego GDB, posso vedere che la lunghezza è 8, sizeof (BBPointI) è anche 8 e i dati sono corretti.

Se guardo lo smontaggio, l'incidente sta accadendo su:

ldrd r2, r3, [r0] 

Il che sembra a posto. r0 contiene 0xb546e, che è l'indirizzo di data. Quando ispeziono quella memoria, posso vedere che contiene i dati che mi aspetto. Per chiunque sia interessato, r2 contiene 72 (non sono sicuro di quello che è) e r3 contiene 8 (molto probabilmente il valore di length).

Qualcuno può far luce su questo problema?

risposta

10

ldrd richiede che l'indirizzo sia allineato a 8 byte. L'idioma di dati * (BBPointI32 *) non è sicuro poiché i dati non sono allineati a 8 byte. Usa memcpy per ottenere i byte nella struct.

+0

Grazie, è perfetto. Il problema * era * relativo alla decisione del compilatore di usare 'ldrd'. Quando lancio 'data' su un' BBPointI32 * 'presuppone che questo significhi che il puntatore era allineato correttamente, ma non lo era. Quindi, al posto di: m_value = * (BBPointI32 *) dati; Ho dovuto usare: memcpy (& m_value, data, length); –

+0

@biorhythmist .. non solo un esilarante twitterer :) – ohhorob

+0

Sto vivendo qualcosa di molto simile. Succede su iPad 3 e _non_ su iPad 2! –

4

Stai usando ARC? Se è così, credo che il problema è che il compilatore è libero di rilasciare decoder dopo la chiamata decodeBytesForKey: (quindi rilasciando il buffer a cui puntava il valore di ritorno).

È lo stesso interior pointer issue garbage collection has. Puoi CFRetain/CFRelease il tuo decoder per estenderne la durata, oppure aggiungi semplicemente un [decoder self] più avanti nel metodo per tenerlo in vita fino a quel punto.

Ho il sospetto che potresti anche essere in grado di risolvere questo problema annotando decoder con __attribute__((objc_precise_lifetime)), ma la mia comprensione di that attribute è un po 'confusa.

3

Il tuo esempio lascia un sacco di variabili da mettere in discussione da qualsiasi potenziale aiutante. Ad esempio: cosa succede se c'è qualcosa di funky con questo unarchiver? La memoria viene gestita correttamente?

Sono riuscito a riprodurre l'incidente che si sta verificando e posso confermare che si verifica solo quando è abilitato -O3 e non quando ad es. Nessuno è selezionato per l'ottimizzazione. Ecco una riduzione del codice che si blocca che elimina variabili esterne come la gestione della memoria dei codificatori, ecc. Si noti che il codice sottostante mantiene intenzionalmente tutti gli oggetti per eliminare la possibilità che l'arresto sia correlato a una sovrascrittenza accidentale, oa un lato- effetto di utilizzare ARC, come suggerito da Andy in un'altra risposta:.

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

penso che questo dà una descrizione più sintetica del problema che chiunque voglia contribuire può utilizzare come base per le immersioni in la mia impressione finora questo è un bug di ottimizzazione in LLVM 3.0, ma forse qualcun altro avrà una teoria migliore su cosa sta succedendo.

Un punto che non hai menzionato nella tua domanda, ma che noto nell'arresto anomalo del mio dispositivo, è che l'errore è accompagnato dalla menzione di un errore EXC_ARM_DA_ALIGN come motivo dell'eccezione di accesso errato.I Googled un post sul blog che sembra alludere agli stessi sintomi e probabilmente motivo di crash come si sta vedendo qui:

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

Infatti, modificando la riga sopra

decodedStruct = *(BBPointI32*)data; 

a

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

Il comportamento anomalo sembra essere alleviato e il codice si comporta come previsto.

+0

Non ho notato EXC_ARM_DA_ALIGN dalla mia parte, ma sì, è la modifica che ho fatto anche io. Grazie per l'aiuto. –

1

Ho ottenuto questo thread, googling "EXC_ARM_DA_ALIGN" e "EXC_BAD_ACCESS". Nessuna delle altre risposte mi ha aiutato, poiché questo errore è saltato fuori a causa di qualcosa di relativamente semplice. Avevo scritto:

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

cioè, mi era stato omesso un @ davanti a una stringa letterale. Rimettendolo in soluzione l'errore.

+0

lol - Ho appena trascorso 2 ore cercando di rintracciare questo. Grazie per aver postato la soluzione. – wufoo

Problemi correlati