2013-10-18 26 views
5

Sto intenzionalmente causando un EXC_BAD_ACCESS. Attivando una scrittura su un NSObject in una pagina di memoria virtuale di sola lettura. Idealmente, mi piacerebbe prendere EXC_BAD_ACCESS, contrassegnare la pagina della memoria virtuale come lettura-scrittura e avere l'esecuzione continua come normalmente avrebbe. È possibile? Il codice che ho scritto per causare il EXC_BAD_ACCESS è sotto.Come posso recuperare da EXC_BAD_ACCESS?

WeakTargetObject.h (ARC)

@interface WeakTargetObject : NSObject 
@property (nonatomic, weak) NSObject *target; 
@end 

WeakTargetObject.m (ARC)

@implementation WeakTargetObject 
@end 

main.m (MRR)

- (void)main { 
    char *mem = NULL; 
    vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE); 
    NSLog(@"mem: %p", mem); 
    WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem); 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSObject *target = [[NSObject alloc] init]; 
    weakTargetObject.target = target; 
    [pool drain]; 
    pool = [[NSAutoreleasePool alloc] init]; 
    NSLog(@"expect non-nil. weakTargetObject.target: %@", weakTargetObject.target); 
    [pool drain]; 

    vm_protect(mach_task_self(), 
      (vm_address_t)mem, 
      vm_page_size, 
      1, 
      VM_PROT_READ); 

    // triggers EXC_BAD_ACCESS when objc runtime 
    // tries to nil weakTargetObject.target 
    [weakTargetObject release]; 
    NSLog(@"expect nil. weakTargetObject.target: %@", weakTargetObject.target); 
} 

risposta

2

Ho trovato un darwin-dev post che ha la risposta!

ATTENZIONE

Questa risposta ha un grave inconveniente. Il mio debugger non funzionerebbe in nessun thread diverso dal thread delle eccezioni mach. Mettere un punto di interruzione in qualsiasi altro thread ha causato l'interruzione di Xcode5. Ho dovuto forzare la chiusura. All'interno della mia funzione catch_exception_raise, ha funzionato bene. I asked the LLDB folks about this.

FINE ATTENZIONE

Questo codice è lo scheletro della risposta. Avrà un ciclo infinito, perché (secondo lo follow-up) è necessario fare qualcosa per rendere l'errore recuperabile. Nel mio caso, ho bisogno di contrassegnare la pagina come lettura-scrittura.

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <stdarg.h> 
#include <pthread.h> 
#include <assert.h> 
#include <mach/mach.h> 

kern_return_t 
catch_exception_raise(mach_port_t exception_port, 
         mach_port_t thread, 
         mach_port_t task, 
         exception_type_t exception, 
         exception_data_t code_vector, 
         mach_msg_type_number_t code_count) 
{ 
    fprintf(stderr, "catch_exception_raise %d\n", exception); 
    return KERN_SUCCESS; // loops infinitely... 
} 

void *exception_handler(void *arg) 
{ 
extern boolean_t exc_server(); 
mach_port_t port = (mach_port_t) arg; 
mach_msg_server(exc_server, 2048, port, 0); 
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns) 
} 

void setup_mach_exception_port() 
{ 
static mach_port_t exception_port = MACH_PORT_NULL; 
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); 
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); 
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); 
pthread_t returned_thread; 
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port); 
} 

void test_crash() 
{ 
    id *obj = NULL; 
    *obj = @"foo"; 
} 

int main(int argc, char** argv) 
{ 
    setup_mach_exception_port(); 
    test_crash(); 
    return 0; 
} 

Questo è il mio nuovo codice che funziona:

WeakTargetObject.h (ARC)

@interface WeakTargetObject : NSObject 
@property (nonatomic, weak) NSObject *target; 
@end 

WeakTargetObject.m (ARC)

@implementation WeakTargetObject 
@end 

Mai n.m (MRR)

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <stdarg.h> 
#include <pthread.h> 
#include <assert.h> 
#include <mach/mach.h> 

static char * mem = NULL; 

kern_return_t 
catch_exception_raise(mach_port_t exception_port, 
         mach_port_t thread, 
         mach_port_t task, 
         exception_type_t exception, 
         exception_data_t code_vector, 
         mach_msg_type_number_t code_count) 
{ 
    fprintf(stderr, "catch_exception_raise %d, mem: %p\n", exception, mem); 
    kern_return_t success = vm_protect(mach_task_self(), 
            (vm_address_t)mem, 
            vm_page_size, 
            0, 
            VM_PROT_DEFAULT); 
    fprintf(stderr, "switched to read-write: %d\n", success); 
    return KERN_SUCCESS; 
} 

void *exception_handler(void *arg) 
{ 
extern boolean_t exc_server(); 
mach_port_t port = (mach_port_t) arg; 
mach_msg_server(exc_server, 2048, port, 0); 
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns) 
} 

void setup_mach_exception_port() 
{ 
static mach_port_t exception_port = MACH_PORT_NULL; 
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); 
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); 
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); 
pthread_t returned_thread; 
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port); 
} 

- (void)main { 
    setup_mach_exception_port(); 
    vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE); 
    NSLog(@"mem: %p", mem); 
    WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem); 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSObject *target = [[NSObject alloc] init]; 
    weakTargetObject.target = target; 
    [pool drain]; 
    pool = [[NSAutoreleasePool alloc] init]; 
    NSLog(@"expect non-nil. weakTargetObject.target: %@", weakTargetObject.target); 
    [pool drain]; 

    vm_protect(mach_task_self(), 
      (vm_address_t)mem, 
      vm_page_size, 
      // zero means don't set VM_PROT_READ as the maximum protection 
      // one means DO set VM_PROT_READ as the maximum protection 
      // we want zero because the if VM_PROT_READ is the maximum protection 
      // we won't be able to set it to VM_PROT_DEFAULT later 
      0, 
      VM_PROT_READ); 

    // triggers EXC_BAD_ACCESS when objc runtime 
    // tries to nil weakTargetObject.target 
    [weakTargetObject release]; 
    NSLog(@"expect nil. weakTargetObject.target: %@", weakTargetObject.target); 
}