Search code examples
ccunit

how do i exit a function which causes segfault and return to calling function


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? */
}

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;
    }