2013-10-18 40 views
5

私は意図的にEXC_BAD_ACCESSを引き起こしています。読み取り専用仮想メモリページ内のNSObjectへの書き込みをトリガすることにより、理想的には、私はEXC_BAD_ACCESSをキャッチし、仮想メモリページを読み書き可能とマークして、通常と同じように実行を続けます。これも可能ですか? EXC_BAD_ACCESSを引き起こすように書いたコードは以下の通りです。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); 
} 

答えて

2

回答があるdarwin-dev postが見つかりました!

WARNING

この答えは大きな欠点があります。私のデバッガはMach例外スレッド以外のスレッドでは動作しません。他のスレッドにブレークポイントを置くと、Xcode5がハングしました。私はそれを強制終了しなければならなかった。私のcatch_exception_raiseの機能の中で、うまくいきました。 I asked the LLDB folks about this.

END警告

このコードは、答えのスケルトンです。エラーを回復可能にするには、(follow-upによると)何かをする必要があるので、無限ループになります。私の場合は、ページを読み書き可能にする必要があります。

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

これは動作します私の新しいコードです:

WeakTargetObject.h(ARC)

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

WeakTargetObject.m(ARC)

@implementation WeakTargetObject 
@end 

マイ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); 
} 
関連する問題