【问题标题】:How do I recover from EXC_BAD_ACCESS?如何从 EXC_BAD_ACCESS 中恢复?
【发布时间】:2013-10-20 14:15:36
【问题描述】:

我故意造成EXC_BAD_ACCESS。通过触发对只读虚拟内存页面中的NSObject 的写入。理想情况下,我想捕捉EXC_BAD_ACCESS,将虚拟内存页面标记为读写,并像往常一样继续执行。这甚至可能吗?我编写的导致EXC_BAD_ACCESS 的代码如下。

WeakTargetObject.h (ARC)

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

WeakTargetObject.m (ARC)

@implementation WeakTargetObject
@end

ma​​in.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);
}

【问题讨论】:

    标签: ios automatic-ref-counting signals virtual-memory mach


    【解决方案1】:

    我找到了有答案的darwin-dev post

    警告

    这个答案有一个很大的缺点。我的调试器不能在除马赫异常线程之外的任何线程中工作。在任何其他线程中放置断点会导致 Xcode5 挂起。我不得不强制退出它。在我的 catch_exception_raise 函数中,它运行良好。 I asked the LLDB folks about this.

    结束警告

    此代码是答案的骨架。它将无限循环,因为(根据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
    

    ma​​in.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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-23
      • 2012-09-16
      • 2019-06-07
      • 2012-10-06
      • 2017-01-22
      相关资源
      最近更新 更多