Question

Hi i am attempting to test a code to see if it is designed to handle memory issues.
Unfortunately I have to do this through c-unit which doesn't handle segmentation faults very well (just crashes out)
So my question is can i add a signal handler to the code i am testing to allow it to exit out of the offending function instead of exiting the entire program?

Essentially can I modify the following handler to exit from function and return to my cunit framework ?

void sighandler(int signo, siginfo_t *si, ucontext_t* context)
{
  printf("Handler executed for signal %d\n", signo);
  exit(0); /* can i replace this with exit from function? */
}
Was it helpful?

Solution

It can be done with a signal handler plus setjmp/longjmp.

I'm not sure how reliable this solution is. It works when I tested it, but may include undefined behavior, so don't rely on it.

It's also important to note that buggy code can do worse things than a segmentation fault. It can, for example, corrupt memory, so your test code, or other tested code, will fail.

The code below is largely based on this answer.

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>

static void good_func(void)
{
    int q, *p = &q;
    printf("%s running\n", __FUNCTION__);
    *p = 3;

}
static void bad_func(void)
{
    int *p = NULL;
    printf("%s running\n", __FUNCTION__);
    *p = 3;
}

static jmp_buf  context;

static void sig_handler(int signo)
{
    longjmp(context, 1);
} 

static void catch_segv(int catch)
{
    struct sigaction sa;

    if (catch) {
        memset(&sa, 0, sizeof(struct sigaction));
        sa.sa_handler = sig_handler;
        sa.sa_flags = SA_RESTART;
        sigaction(SIGSEGV, &sa, NULL);
    } else {
        sigemptyset(&sa);
        sigaddset(&sa, SIGSEGV);
        sigprocmask(SIG_UNBLOCK, &sa, NULL);
    }
}

typedef void (*func_t)(void);

static int safe_run(func_t func)
{
    catch_segv(1);

    if (setjmp(context)) {
            catch_segv(0);
            return 0;
    }

    func();
    catch_segv(0);
    return 1;
}

int main()
{
    printf("good_func is %s\n", safe_run(good_func) ? "good" : "bad");
    printf("bad_func is %s\n",  safe_run(bad_func) ?  "good" : "bad");
    return 0;
}

OTHER TIPS

Here's a similar question with an answer (albeit the wrong language, C++): Catch Segfault or any other errors/exceptions/signals in C++ like catching exceptions in Java

I believe that what you're looking for is an exception in place of your exit(0) statement, but a Segmentation Fault is not an exception, therefore making it difficult to deal with. This being said, it appears that C does not have built in exception handling.

Regardless, here's a reference about receptions in c++: http://www.cplusplus.com/doc/tutorial/exceptions/

I don't know exactly how you're using the function you have here, but perhaps you can return a boolean instead of void, and return from the calling function if the boolean is true?

If you know exactly what in your function is expected to cause a SEGV, you can try setting a flag in your signal handler. (Actually, note that the set of functions you're allowed to call from within a signal handler is extremely limited, so setting a flag is practically all you can do anyway.)

Declare your flag variable as volatile int caused_segv = 0, let your signal handler set caused_segv = 1, and in your code, after performing the action that may raise SIGSEGV, check if caused_segv is nonzero.

Also, note that per POSIX.1, SIGFPE, SIGILL, and SIGSEGV are explicitly called out such that when the signal handler completes, the behavior is undefined. For any other signal, "the program shall resume execution at the point it was interrupted."

So, the answer to your question really is, You can't reliably do what you're trying to do because getting a SIGSEGV automatically leaves you in an Undefined Behavior state, but you can use what I gave you above to try.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top