2013-10-18 15 views
5

Ich verursache absichtlich eine EXC_BAD_ACCESS. Durch Auslösen eines Schreibvorgangs auf eine NSObject in einer schreibgeschützten virtuellen Speicherseite. Idealerweise möchte ich EXC_BAD_ACCESS fangen, die virtuelle Speicherseite als Lese-Schreib-Speicher markieren und die Ausführung so fortsetzen, wie es normalerweise der Fall wäre. Ist das überhaupt möglich? Der Code, den ich geschrieben habe, um die EXC_BAD_ACCESS zu verursachen, ist unten.Wie kann ich von EXC_BAD_ACCESS wiederherstellen?

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

Antwort

2

Ich fand eine darwin-dev post, die die Antwort hat!

WARNUNG

Diese Antwort hat einen großen Nachteil. Mein Debugger würde in keinem anderen Thread als dem Mach-Ausnahme-Thread funktionieren. Wenn Sie einen Haltepunkt in einen anderen Thread einfügen, wurde Xcode5 angehalten. Ich musste es aufgeben. Innerhalb meiner catch_exception_raise Funktion funktionierte es gut. I asked the LLDB folks about this.

Endwarnungs

Dieser Code ist das Skelett der Antwort. Es wird Endlosschleife, weil Sie (gemäß der follow-up) müssen Sie etwas tun, um den Fehler behebbar zu machen. In meinem Fall muss ich die Seite als schreibgeschützt markieren.

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

Das ist mein neuer Code, der funktioniert:

WeakTargetObject.h (ARC)

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

WeakTargetObject.m (ARC)

@implementation WeakTargetObject 
@end 

Mai Nr. 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); 
}