Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a C program continue execution after a signal is handled?

Tags:

c

signals

I'm new at signal handling in Unix through C and I have been looking at some tutorials on it (out of pure interest).

My questions is, is it possible to continue execution of a program past the point where a signal is handled?

I understand that the signal handling function does the cleanup but in the spirit of exception handling (such as in C++), is it possible for that signal to be handled in the same fashion and for the program to continue running normally?

At the moment catch goes in an infinite loop (presumably a way to quit would be to call exit(1) ).

My intention would be for b to be assigned 1 and for the program to finish gracefully (if that is possible of course).

Here's my code:

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

int a = 5;
int b = 0;

void catch(int sig)
{
    printf("Caught the signal, will handle it now\n");
    b = 1;
}

int main(void)
{
    signal(SIGFPE, catch);

    int c = a / b;

    return 0;
}

Also, as C is procedural, how come the signal handler declared before the offending statement is actually called after the latter has executed?

And finally, in order for the handling function to do its clean up properly, all the variables than need to be cleaned up in the event of an exception need to be declared prior to the function, right?

Thanks in advance for your answers and apologies if some of the above is very obvious.

like image 501
Nobilis Avatar asked Jan 09 '13 10:01

Nobilis


People also ask

Can a signal handler be interrupted?

Signal handlers can be interrupted by signals, including their own. If a signal is not reset before its handler is called, the handler can interrupt its own execution. A handler that always successfully executes its code despite interrupting itself or being interrupted is async-signal-safe.

What happens when we register a handler for a signal?

In the handler function, the signal handler re register to SIG_DFL for default action of the signal. When user typed Ctrl+C for second time, the process is terminated which is the default action of SIGINT signal.

What does signal function do in C?

Description. The C library function void (*signal(int sig, void (*func)(int)))(int) sets a function to handle signal i.e. a signal handler with signal number sig.

Can you catch sigint?

When Ctrl+C is pressed, SIGINT signal is generated, we can catch this signal and run our defined signal handler. C standard defines following 6 signals in signal.


2 Answers

Yes, that's what signal handlers are for. But some signals need to be handled specially in order to allow the program to continue (e.g. SIGSEGV, SIGFPE, …).

See the manpage of sigaction:

According to POSIX, the behavior of a process is undefined after it ignores a SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(2) or raise(3). Integer division by zero has undefined result. On some architectures it will generate a SIGFPE signal. (Also dividing the most negative integer by -1 may generate SIGFPE.) Ignoring this signal might lead to an endless loop.

Right now, you are ignoring the signal, by not doing anything to prevent it from happening (again). You need the execution context in the signal handler and fix it up manually, which involves overwriting some registers.

If SA_SIGINFO is specified in sa_flags, then sa_sigaction (instead of sa_handler) specifies the signal-handling function for signum. This function receives the signal number as its first argument, a pointer to a siginfo_t as its second argument and a pointer to a ucontext_t (cast to void *) as its third argument. (Commonly, the handler function doesn't make any use of the third argument. See getcontext(2) for further information about ucontext_t.)

The context allows access to the registers at the time of fault and needs to be changed to allow your program to continue. See this lkml post. As mentioned there, siglongjmp might also be an option. The post also offers a rather reusable solution for handling the error, without having to make variables global etc.:

And because you handle it youself, you have any flexibility you want to with error handling. For example, you can make the fault handler jump to some specified point in your function with something like this:

 __label__ error_handler;   
 __asm__("divl %2"      
         :"=a" (low), "=d" (high)       
         :"g" (divisor), "c" (&&error_handler))     
 ... do normal cases ...

 error_handler:     
     ... check against zero division or overflow, so  whatever you want to ..

Then, your handler for SIGFPE needs only to do something like

context.eip = context.ecx;

like image 94
Jonas Schäfer Avatar answered Oct 29 '22 16:10

Jonas Schäfer


If you know what you are doing, you can set the instruction pointer to point right after the offending instruction. Below is my example for x86 (32bit and 64bit). Don't try at home or in real products !!!

#define _GNU_SOURCE /* Bring REG_XXX names from /usr/include/sys/ucontext.h */

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ucontext.h>

static void sigaction_segv(int signal, siginfo_t *si, void *arg)
{
    ucontext_t *ctx = (ucontext_t *)arg;

    /* We are on linux x86, the returning IP is stored in RIP (64bit) or EIP (32bit).
       In this example, the length of the offending instruction is 6 bytes.
       So we skip the offender ! */
    #if __WORDSIZE == 64
        printf("Caught SIGSEGV, addr %p, RIP 0x%lx\n", si->si_addr, ctx->uc_mcontext.gregs[REG_RIP]);
        ctx->uc_mcontext.gregs[REG_RIP] += 6;
    #else
        printf("Caught SIGSEGV, addr %p, EIP 0x%x\n", si->si_addr, ctx->uc_mcontext.gregs[REG_EIP]);
        ctx->uc_mcontext.gregs[REG_EIP] += 6;
    #endif
}

int main(void)
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sigaction_segv;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGSEGV, &sa, NULL);

    /* Generate a seg fault */
    *(int *)NULL = 0;

    printf("Back to normal execution.\n");

    return 0;
}
like image 30
Alexey Polonsky Avatar answered Oct 29 '22 16:10

Alexey Polonsky