分段违规后恢复生机

Mar*_*son 14 c signals exception-handling segmentation-fault

在Segmentation Fault错误之后,是否可以恢复C程序的正常执行流程?

struct A {
    int x;
};
A* a = 0;

a->x = 123; // this is where segmentation violation occurs

// after handling the error I want to get back here:
printf("normal execution");
// the rest of my source code....
Run Code Online (Sandbox Code Playgroud)

我想要一个类似于NullPointerException的机制,它存在于Java,C#等中.

注意:请不要告诉我C++中有一个异常处理机制,因为我知道,不要告诉我在分配之前我应该​​检查每个指针.

我真正想要实现的是恢复正常的执行流程,如上例所示.我知道可以使用POSIX信号进行一些操作.应该怎么样?其他想法?

nos*_*nos 23

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <signal.h>
#include <stdlib.h>
#include <ucontext.h>

void safe_func(void)
{
    puts("Safe now ?");
    exit(0); //can't return to main, it's where the segfault occured.
}

void
handler (int cause, siginfo_t * info, void *uap)
{
  //For test. Never ever call stdio functions in a signal handler otherwise*/
  printf ("SIGSEGV raised at address %p\n", info->si_addr);
  ucontext_t *context = uap;
  /*On my particular system, compiled with gcc -O2, the offending instruction
  generated for "*f = 16;" is 6 bytes. Lets try to set the instruction
  pointer to the next instruction (general register 14 is EIP, on linux x86) */
  context->uc_mcontext.gregs[14] += 6; 
  //alternativly, try to jump to a "safe place"
  //context->uc_mcontext.gregs[14] = (unsigned int)safe_func;
}

int
main (int argc, char *argv[])
{
  struct sigaction sa;
  sa.sa_sigaction = handler;
  int *f = NULL;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO;
  if (sigaction (SIGSEGV, &sa, 0)) {
      perror ("sigaction");
      exit(1);
  }
  //cause a segfault
  *f = 16; 
  puts("Still Alive");
  return 0;
}

$ ./a.out
SIGSEGV raised at address (nil)
Still Alive
Run Code Online (Sandbox Code Playgroud)

如果我在生产代码中看到这样的东西,我会用蝙蝠击败某人,但这是一个丑陋,有趣的黑客.您不知道段错误是否已损坏您的某些数据,您将无法恢复并且知道现在一切正常,没有可行的方法来执行此操作.你可以做的唯一温和的事情是尝试记录错误(直接使用write(),而不是任何stdio函数 - 它们不是信号安全的)并且可能重启程序.对于那些情况,你最好写一个监视子进程退出的超级进程进程,记录它并启动一个新的子进程.

  • 您应该修改EIP(x86 32位)或RIP寄存器(x86_64).它们位于uc_mcontext.gregs []中的不同偏移处.因此,如果在#ifdef __WORDSIZE == 64下使用gregs [REG_RIP]而不是gregs [14],否则使用gregs [REG_EIP] (2认同)

Mac*_*ade 7

您可以使用信号处理程序捕获分段错误,并决定继续执行程序(风险自负).

信号名称是SIGSEGV.

您必须sigaction()从signal.h标题中使用该函数.

基本上,它的工作方式如下:

struct sigaction sa1;
struct sigaction sa2;

sa1.sa_handler = your_handler_func;
sa1.sa_flags   = 0;
sigemptyset( &sa1.sa_mask );

sigaction( SIGSEGV, &sa1, &sa2 );
Run Code Online (Sandbox Code Playgroud)

这是处理函数的原型:

void your_handler_func( int id );
Run Code Online (Sandbox Code Playgroud)

如您所见,您无需返回.程序的执行将继续,除非您决定自己从处理程序中停止它.

  • 简单回来无济于​​事; 相同的指令将再次执行并再次崩溃.一种更可靠的做事方式(但仍然很难看,不推荐!)是将`longjmp`(或更好的`siglongjump`)从信号处理程序中移出到已知安全的位置. (16认同)