4

I am new to this type of programming, so sorry if my question is trivial. What I am trying to do is to cause a segmentation fault in my program and instead of exiting the program, I want to handle the signal and continue execution after segmentation fault. I wrote a code that seem to be working, I just want to make sure that this is the way to do this. So here is my code.

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP]++;
}


int main(int argc, char *argv[])
{
   struct sigaction action;

  action.sa_handler=myhandle;
  sigaction(11,&action,NULL);

  printf("Before segfault\n");

  int *a=NULL;
  int b=*a;

  printf("I am still alive\n");

  return 0;
}

Can someone explain to me why the printf inside myhandle runs twice ? Also is this code ok ?

Thank you.

Saik
  • 993
  • 1
  • 16
  • 40
  • Avoid to use hard-coded values and use the already existing `SIGSEGV` (i.e., instead of `11`). The list of signals can be found [here](http://man7.org/linux/man-pages/man7/signal.7.html). – SRG Dec 09 '18 at 12:38

5 Answers5

4

By this example i have modified your code at below way and now it works as you wanted.

#include<stdio.h>
#define __USE_GNU
#include<signal.h>
#include<ucontext.h>

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP] = context->uc_mcontext.gregs[REG_RIP] + 0x04 ;
}


int main(int argc, char *argv[])
{

  struct sigaction action;
  action.sa_sigaction = &myhandle;
  action.sa_flags = SA_SIGINFO;

  sigaction(11,&action,NULL);


  printf("Before segfault\n");

  int *a=NULL;
  int b;
  b =*a;

  printf("I am still alive\n");

  return 0;
}

Output:

jeegar@jeegar:~/stackoverflow$ gcc test1.c
jeegar@jeegar:~/stackoverflow$ ./a.out 
Before segfault
Signal is 11
I am still alive

On further question form OP in commentes. To runtime remove this handler for this signel

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

if(flag == 0) {
  // Disable the handler
  action.sa_sigaction = SIG_DFL;
  sigaction(11,&action,NULL);
}

if(flag) {
  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP] = context-      >uc_mcontext.gregs[REG_RIP] + 0x04 ;
}

}
Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222
  • So here is my problem, this code was just an example. I am working on program, which is about 3000 lines long. What I am trying to do is that if the flag to ignore is set, then I want to ignore the signal, no matter where it is coming from. How can this be done ? – Saik Jan 25 '16 at 06:46
  • that flag is compile time or run time? if run time then use it in handler function or any other logic – Jeegar Patel Jan 25 '16 at 06:50
  • the flag is run time. When the flag is not set and I used it in handler, sometimes it gets into an infinite loop. Any idea why this might happen ? basically if flag is not set, I just want the program to end in segfault, but it gets caught in infinite loop. – Saik Jan 25 '16 at 07:17
  • @Saik Added some more code in answer for your help. Make it work for your case. – Jeegar Patel Jan 25 '16 at 08:33
  • The second example is not correct. You must use the `.sa_handler` field of `struct sigaction` for the `SIG_DFL` assignment, as per the description of [sigaction](http://man7.org/linux/man-pages/man2/sigaction.2.html). In addition, please, use the POSIX.1-1990 standard `SIGSEGV` signal code instead of `11`, which is a hard-coded value (see [signal](http://man7.org/linux/man-pages/man7/signal.7.html)). – SRG Dec 09 '18 at 13:01
  • Note that `printf` from a signal handler isn't guaranteed to be safe. Catching SIGSEGV is already living dangerously, so this may be ok for a toy example, but should be commented. See also [Why is a segmentation fault not recoverable?](https://stackoverflow.com/q/70258418) – Peter Cordes Nov 04 '22 at 20:16
2

You may need to check dissamembly to find PC (i.e. RIP) to jump. For your case, it should look like,

    int *a=NULL;
  400697:       48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
  40069e:       00
    int b=*a;
  40069f:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4006a3:       8b 00                   mov    (%rax),%eax
  4006a5:       89 45 f4                mov    %eax,-0xc(%rbp)

    printf("I am still alive\n");
  4006a8:       bf 7c 07 40 00          mov    $0x40077c,%edi
  4006ad:       e8 de fd ff ff          callq  400490 <puts@plt>

, and the exception is on 0x4006a3, it should be set to 0x4006a8 to jump to printf(). Or +2 to 0x4006a5, which is also valid.

The reason for message dumping twice is because, when first time calling,

context->uc_mcontext.gregs[REG_RIP]++

, it sets RIP to 0x4006a4, one invalid location, triggers one more exception.

Eric
  • 471
  • 4
  • 9
  • how you have dissamembly this code? any tool or utility? – Jeegar Patel Jan 25 '16 at 06:20
  • 1
    It's really machine/compiler dependent. For example, with gcc on linux, you could try, "gcc -g -o " to compile with debug information, then do "objdump -S " to display source code intermixed with disassembly. – Eric Jan 25 '16 at 06:24
1

Can someone explain to me why the printf inside myhandle runs twice ?

The behavior appears OS-dependent. The control from myhandle may not return to main at all.

It's unusual to catch signal 11, normally that is handled by the OS to terminate the program.

However, it's possible to write a signal handler for it and let that function to print out something before exit.

struct sigaction action;
struct sigaction old_action;

void myhandle( int mysignal )
{
    if( 11 == mysignal )
    {
        printf( "Signal is %d\n", mysignal );   // <-- this should print OK.
        sigaction( 11, &old_action, NULL );     // restore OS signal handler, or just exit().
        return;
    }
}


int main(int argc, char *argv[])
{
    action.sa_handler = myhandle;
    sigaction( 11, &action, &old_action );

    printf("Before segfault\n");

    int *a=NULL;
    int b=*a;

    printf( "I am still alive\n" );   // <-- this won't happen
    return 0;
}
artm
  • 17,291
  • 6
  • 38
  • 54
1

These kinds of signals is not trivial to handle for continued execution, at all. The reason is that the instruction causing the signal has not been executed and therefore the execution will continue by trying to execute the failing instruction.

The reason the signal handler execute twice (or even repeats indefinitely) is that returning would cause the CPU to retry to execute the same thing that resulted in segmentation fault before, and without anything changed it will result in segmentation fault again.

In order to handle such a signal (SIGSEGV, SIGFPE, SIGILL etc) you will have to actually alter the signal context to resolve the problem. To do this you will need to use code specially crafted for the CPU in use and also use compiler specific behavior since you will need to modify the context.

skyking
  • 13,817
  • 1
  • 35
  • 57
1

Not sure if this has been said, but it is NOT SAFE to call printf() from a signal handler, because of its use of malloc().

Please read more to understand what functions are safe from with the signal handling context. On Linux and also on BSD/MacOS, an alternative is to arrange for signal delivery on a special file descriptor-- on Linux, this is done via the signalfd family of functions, and then you can handle the signal in a normal event loop.

Michael Kerrisk's book on Linux is a fantastic reference here.

Tom Dial
  • 59
  • 4