0

I have a simple code which uses two signal handlers for "Segmentation fault" signal. First one works when signal happens and after longjmp, I do reassigning of handler with second one for that signal. Unfortunately, the flow of code doesn't reach necessary handler and I still get "Segmentation fault".

#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

int i;
int ci;

jmp_buf m_env_buffer;
jmp_buf max_env_buffer;

void handler(int signum){
    printf("sigsegv on i:[%d]", i);
    ci = (++i);
    longjmp(m_env_buffer,1);
}

void top_handler(int signum){
    printf("sigsegv on max i:[%d]", i);
    longjmp(max_env_buffer,10);
}

int main(void) {
    signal(SIGSEGV, handler);
    char * pstart = "loremipsum";

    int m_cell = 0;
    char m_cell_v;

    int point;

    point = setjmp(m_env_buffer);
    if(point == 0){
            for(i=0; ;i--){
                    m_cell_v = pstart[i];
            }
    }

    //this wasn't invoked
    signal(SIGSEGV, top_handler);
    point = setjmp(max_env_buffer);
    if(point == 0){
            for(i=ci; ;i++){
                    char cur = pstart[i];
                    if(cur==10)
                            printf("\n");
                    printf("%c",cur);
            }
    }
    puts("finish");
    return 0;
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Pirate
  • 97
  • 8
  • 2
    `printf` is not among the functions you may safely call from a signal handler. That is, it is not async-signal-safe. That might or might not be directly related to your observed behavior. – John Bollinger Jun 23 '19 at 12:11
  • You are (intentionally, it seems) producing *undefined* behavior. That doesn't stop just because you catch a SIGSEGV. Your program cannot expect to continue afterward as if nothing happened. (Though it might do, because "undefined".) – John Bollinger Jun 23 '19 at 12:20
  • Assuming you're running on Linux, first, [don't use `signal()`, use `sigaction()` instead](https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal). Second, **read** and **understand** the [`setjmp()`/`longjmp()` man page](http://man7.org/linux/man-pages/man3/longjmp.3.html). – Andrew Henle Jun 23 '19 at 15:07
  • If you're doing long jumps from a signal handler, consider whether you should be using [`sigsetjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsetjmp.html) and [`siglongjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjmp.html) rather than [`setjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html) and [`longjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html). – Jonathan Leffler Jun 23 '19 at 19:32

1 Answers1

0

signal() should not be used. It has unreliable semantics. On your system the default action (termination) is performed when the second SIGSEGV is received. The second call to signal() has effectively no effect.

You should use sigaction() instead. In your case you can use the following function instead of signal():

void set_signal (int signum, void (*handler)(int))
{
    struct sigaction act;

    act.sa_handler = handler;
    sigemptyset (&act.sa_mask);
    act.sa_flags = SA_NODEFER;
    act.sa_restorer = NULL;

    sigaction (signum, &act, NULL);
}

In the future, read the documentation at your disposal. A good ressource is the glibc manual. It has a good chapter on signal handling.

  • while it's absolutely correct that `signal(2)` shouldn't be used, that isn't *it*. The second `SEGV` handler won't be called even if the OP's code is fixed to use `sigaction(2)` and not call signal-unsafe stdio functions while handling signals. –  Jun 23 '19 at 15:30
  • Yup, I didn't noticed the `SA_NODEFER`. –  Jun 23 '19 at 16:34