In the following code, I am handling SIGSEGV signal and fixing the
error condition by minimizing the bound variable. I guess whenever the signal handler returns it restarts the instruction during which the signal occurred. In signal handler I minimized the bound variable but still getting SIGSEGV and going in infinite loop
Not sure where I am going wrong
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void SIG_segfault_handler(int);
static int bound = 5800000;
int main(int argc,char *argv[])
{
signal(SIGSEGV,SIG_segfault_handler);
puts("This is the driver code for experimenting with signal");
int *array = malloc(50);
*(array+ 23) = 78;
puts("-------------------------------------------------------------------------------------");
*(array + bound) = 100;
printf("Data: %d , %d \n",*(array + 23),*(array + bound));
puts("Initialiazed array with 50 bytes.. \n End of driver code. ");
//free(array);
return 0;
}
void SIG_segfault_handler(int signum)
{
puts("This is segmentation fault.. cannot continue with the memory operation.. aborting");
puts("Signal SIGSEGV is handled by the program");
puts("Fixing the error conditions..");
bound = 2;
}
It's undefined behaviour per C and POSIX standards. Returning from a signal handler of SIGSEGV is not allowed.
See 7.14.1.1 The signal function:
When a signal occurs and func points to a function, it is
implementation-defined whether the equivalent of signal(sig, SIG_DFL);
is executed or the implementation prevents some implementation-defined
set of signals (at least including sig) from occurring until the
current signal handling has completed; in the case of SIGILL, the
implementation may alternatively define that no action is taken. Then
the equivalent of (*func)(sig); is executed. If and when the function
returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other
implementation-defined value corresponding to a computational
exception, the behavior is undefined; otherwise the program will
resume execution at the point it was interrupted.
After executing the signal handler, the execution restarts the same instruction which caused the SIGSEGV. That means when you overflow the array (which causes SIGSEGV) will be executed again after returning
from the signal handler. This should explain the infinite loop.
Besides, you shouldn't call puts() from a signal signal handler; you are only allowed to call async-signal-safe functions from within a signal handler. See the POSIX manual for the list of async-signal-safe functions.
Use of signal() is also discouraged due to different semantics provided by various implementations. For this reason (among others), it's recommended to use sigaction() for installing signal handlers.
Related
I'm learning to write a signal handler in C for a Linux system. Here's my code:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
//Return type of the handler function should be void
printf("\nInside handler function\n");
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
My question is, why when I hit Ctrl+C twice, the program stops? Shouldn't it be that everytime I hit Ctrl+C the signal handler runs, so effectively the program should run forever?
In reality, this is my output, the signal handler is only called in the first Ctrl+C, not the second time:
1 : Inside main function
2 : Inside main function
3 : Inside main function
4 : Inside main function
^C
Inside handler function
5 : Inside main function
6 : Inside main function
7 : Inside main function
8 : Inside main function
9 : Inside main function
10 : Inside main function
^C
On Linux, a number of factors contribute to the behaviour of signal. Depending on the version of glibc, the defined feature_test_macros(7), and whether or not the kernel version of the function is used, the results will differ between two polarizing behaviours, described as System V semantics and BSD semantics:
With System V semantics, when a signal handler is invoked, the disposition of the signal is reset to its default behaviour (SIG_DFL). Additionally, further delivery of the signal is not blocked during the execution of the signal handler.
With BSD semantics, when a signal handler is invoked, the disposition of the signal is not reset, and further delivery of the signal is blocked during the execution of the signal handler. Additionally, certain system calls will be automatically be restarted if interrupted by the signal handler.
Your version of signal appears to be supplying System V semantics. The signal disposition is reset after the first signal handler, and the following SIGINT terminates the program.
See signal(2) and its notes on portability for more details.
See signal-safety(7) for a list of functions that are safe to call from within a signal handler. printf is not an async-signal-safe function, and should not be called from within a signal handler.
Use write(2) instead.
The quickest fix is to call signal within the signal handler to once again set the signal disposition to the signal handler.
void sig_handler(int signum)
{
(void) signum;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
signal(SIGINT, sig_handler);
}
The more robust solution is to use sigaction(2) to establish signal handlers, as its behaviour is more consistent and provides better portability.
A basic example:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
volatile sig_atomic_t sig_count = 0;
void sig_handler(int signum)
{
(void) signum;
sig_count++;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
}
int main(void)
{
struct sigaction sa = { .sa_handler = sig_handler };
sigaction(SIGINT, &sa, NULL);
while (sig_count < 5);
}
The default behaviour of sigaction is similar to that described by BSD semantics, with the exception that certain system calls will not restart when interrupted by the signal handler.
To enable this behaviour, the .sa_flags member of the struct sigaction should contain the SA_RESTART flag.
To mimic System V semantics, the .sa_flags member of the struct sigaction should contain the SA_RESETHAND and SA_NODEFER flags.
TL;DR: use sigaction(), not signal(), to install signal handlers.
As comments and other answers have observed, your signal handler has undefined behavior as a result of its call to printf(). In the event that the signal handler is ever triggered, that gives the whole program UB, which makes it very difficult to reason about its observed behavior.
But consider this variation instead:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
I have switched from printf to write inside the signal handler, thereby removing the UB. And if I compile it with
gcc -std=c11 htest.c -o htest
then the resulting executable still exhibits the behavior you describe: the first Ctrl-C is handled, but the second is not.
HOWEVER, if I instead compile with
gcc htest.c -o htest
then the resulting program intercepts every Ctrl-C I type, as I guess you were expecting. So what's going on?
The problem revolves around the fact that the details of the behavior of the signal() function have varied historically. For more detail, see the portability notes in the signal(2) manual page, but here's a brief rundown:
In the original System V UNIX, the custom signal handlers installed via signal() were one-shots: when such a handler was triggered, the disposition for the signal was reset to its default, and the signal was not blocked during execution of the handler.
The System V behavior has some issues, so BSD implemented signal() differently in these respects: signal disposition is not automatically reset, and the signal is blocked during execution of the handler. And additionally, in BSD, certain blocking system calls are automatically restarted if interrupted by a signal that does not result in the program terminating.
These differences mean that the only portable uses for the signal() function are for setting signal disposition to SIG_DFL or SIG_IGN.
Glibc supports both alternatives, and which one you get is controlled by feature test macros, which can be influenced by compiler command-line options. It defaults to BSD semantics as long as the _DEFAULT_SOURCE macro is defined (_BSD_SOURCE prior to glibc 2.19). Gcc defines that macro by default, but some command line options, notably the strict-conformance -std options, cause Gcc not to define it. And that's why I could get different behavior depending on how I compiled the program.
On POSIX systems, the solution is to use the sigaction() function instead of signal() to register signal handlers. This function has well-defined semantics for all the areas of behavioral difference described above, including a well-defined means to choose among them. However, to get its declaration included in all cases, you will need to ensure that a different feature-test macro is defined. For example:
// Manipulation of feature-test macros should precede all header inclusions
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 1
#endif
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum) {
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(void) {
// Register signal handler
struct sigaction sa = { .sa_handler = sig_handler /* default sa_mask and sa_flags */ };
sigaction(SIGINT, &sa, NULL);
while (int i = 1; ; i++) {
printf("%d : Inside main function\n",i);
sleep(1);
}
return 0;
}
That will reliably get you a handler that is not reset when triggered, does have SIGINT blocked while it is being handled, and does not automatically restart system calls interrupted by the signal. That will prevent the program from being killed via a SIGINT, though there are other ways it can be killed, such as via a SIGKILL (which cannot be blocked or handled).
My question is, why when I hit Ctrl+C twice, the program stops?
Undefined behaviour:
Your code invokes undefined behaviour because it calls a function (printf) that isn't async-signal-safe.¹
From C11:
If the signal occurs other than as the result of calling the abort or
raise function, the behavior is undefined if the signal handler refers
to any object with static or thread storage duration that is not a
lock-free atomic object other than by assigning a value to an object
declared as volatile sig_atomic_t, or the signal handler calls any
function in the standard library other than the abort function, the
_Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the
signal that caused the invocation of the handler.
As per the C Standard, you can only call:
abort()
_Exit()
quick_exit()
signal() /* With the first argument equal to the signal number the handler caught */
safely inside a signal handler.
The POSIX standard, however, specifies many more functions. So you can have write(), but not printf().
[1] — An async-signal-safe function is one that can be safely called from within a signal handler. (Linux man page)
Don't use signal(). Instead use sigaction().
Per the spec, [w]hen a signal occurs, and [the second argument to signal] points to a function, it is implementation-defined whether the equivalent of a
signal(sig, SIG_DFL); is executed...
Historically, some UNIX flavors reset the signal disposition to the default behavior upon entry into the user-defined signal handler. That meant that your first SIGFOO would invoke your handler, but your next one would trigger SIG_DFL behavior. Importantly, this is consistent with what you observe.
Yes, printf() is unsafe (undefined behavior) to call in a signal handler. In the code you provide, it might conceivably interrupt itself, which can lead to all sorts of nastiness. In practice, people call printf() all the time in trivial programs to no great harm. The fact that your behavior is repeatable suggests that you are getting deterministic behavior, and not dreadful eldritch magic undefined behavior.
sigaction() forces you to specify what kind of behavior you want when the user-supplied handler is invoked, and is thus superior to signal().
I am trying to learn Signals. I know invalid memory access will cause segfault. So, I register a signal handler for SIGSEGV signal.
#include <stdio.h>
#include <signal.h>
void sighandler(int signum)
{
printf("%s\n", __func__);
}
int main()
{
int *a = NULL;
signal(SIGSEGV, sighandler);
*a = 5;
return 0;
}
Running this code, I am continuously getting SIGSEGV Signals. I thought i should only get the signal once. Can you guys explain why I am getting signals continuously
After the SEGV handler finishes, the instruction that triggered re-executes. Since you didn't do anything to prevent the next execution from faulting, you get SEGV again, ad infinitum.
See more in this answer.
The signal handler is returning to instruction that triggered it namely *a = 5 which is causing it to loop.
You have several problems including the use of printf inside a signal handler.
There are safe and not-safe ways of dealing with this
NOTES
Using signal(2) is not recommended for signal handling in general.
Handling SIGSEGV is even more complicated because of the way the signal semantics work. Quoting from the man page:
The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal()
to establish a signal handler vary across
systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a
signal handler is invoked; use that interface instead of signal().
So the first thing you should do is use sigaction.
Next, handling SIGSEGV is a weird beast:
How to write a signal handler to catch SIGSEGV?
and
Does linux allow any system call to be made from signal handlers?
have good answers and get into specific details. There are external links in some of the answers given there.
How to do this using signal(2)
Well :-) let's say you want to use signal(2) and you want to play with this in a weird way....
You can use sigjmpset and siglongjmp.
sigjmpset marks a point where siglongjmp should jump to. The first time sigjmpset is called (to set the point) it returns 0. When siglongjmp jumps to it, (which means it gets called again as a result of the long jump), it returns 1.
Which means we can do this:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
sigjmp_buf env;
int sigsav;
void sighandler(int signum)
{
const char msg[] = "Skipping signal\n";
write(2, msg, sizeof(msg));
siglongjmp(env, sigsav);
}
int main()
{
int *a = NULL;
signal(SIGSEGV, sighandler);
if(!sigsetjmp(env, sigsav)) {
printf("setting value of a\n");
*a = 5;
}
else {
printf("returned to sigsetjmp, but now we skip it!\n");
}
return 0;
}
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.
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;
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;
}
In general, yes, execution continues after the handler returns. But if the signal was caused by a hardware error (such as a floating point exception or a segmentation fault), you have no way of undoing that error, and so your program will be terminated regardless.
In other words, you have to distinguish between signals and things that cause signals. Signals by themselves are perfectly fine and handlable, but they don't always let you fix errors that cause signals.
(Some signals are special, such as ABRT and STOP, in the sense that even if you just raise such a signal manually with kill, you still can't "prevent its effects". And of course KILL cannot even be handled at all.)
Doing homework with signals and fork and have a problem with the signal.
I've created the function:
void trata_sinal_int() {
char op[2];
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y') {
printf("Bye Bye\n");
exit(0);
}
}
And in main I have:
signal(SIGINT, trata_sinal_int);
When I run this, and press CTRL ^C the function void trata_sinal_int() is called and I got the message.
If I press y program ends as expected but if I press n program still ends.
It is not returning to were he was before pressing CTRL ^C.
Is this supposed to happen?
It depends on which standard you are adhering to, but Standard C doesn't allow you to do much more than modify a variable of type volatile sig_atomic_t or call _Exit (or abort() or signal()) from a signal handler. POSIX is a lot more lenient. Your code in your signal handler, replete with user interaction, is pushing beyond the limits of what even POSIX allows. Normally, you want your signal handler function to be small and svelte.
Note that the signal handler function should be:
void trata_sinal_int(int signum)
{
This allows you to compile without casts or compiler warnings about type mismatches.
The signal() function may reset the signal handler back to default behaviour when it is invoked; classically, it is necessary to reinstate the signal handler inside the signal handler:
signal(signum, trata_sinal_int);
So far, that's all pretty generic and semi-trivial.
When you type the Control-C, the system does go back to roughly where it was when the signal was originally received. However, what happens next depends on where it was (one of the reasons you have to be so very careful inside the handler). For example, if it was in the middle of manipulating the free list pointers inside malloc(), it would return there, but if you'd reinvoked malloc() inside the handler, all hell might be breaking loose. If you were inside a system call, then your call may be interrupted (return with an error indication and errno == EINTR), or it may resume where it left off. Otherwise, it should go back to where the calculation was running.
Here's (a fixed up version of) your code built into a test rig. The pause() function waits for a signal before returning.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
static void trata_sinal_int(int signum)
{
char op[2];
signal(signum, trata_sinal_int);
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y')
{
printf("Bye Bye\n");
exit(0);
}
}
int main(void)
{
signal(SIGINT, trata_sinal_int);
for (int i = 0; i < 3; i++)
{
printf("Pausing\n");
pause();
printf("Continuing\n");
}
printf("Exiting\n");
return(0);
}
I should really point out that the scanf() is not very safe at all; a buffer of size 2 is an open invitation to buffer overflow. I'm also not error checking system calls.
I tested on Mac OS X 10.7.5, a BSD derivative. The chance are good that the resetting of signal() would be unnecessary on this platform, because BSD introduced 'reliable signals' a long time ago (pre-POSIX).
ISO/IEC 9899:2011 §7.14.1.1 The signal function
¶5 If the signal occurs other than as the result of calling the abort or raise function, the
behavior is undefined if the signal handler refers to any object with static or thread
storage duration that is not a lock-free atomic object other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal handler calls any function
in the standard library other than the abort function, the _Exit function, the
quick_exit function, or the signal function with the first argument equal to the
signal number corresponding to the signal that caused the invocation of the handler.
Furthermore, if such a call to the signal function results in a SIG_ERR return, the
value of errno is indeterminate.252)
252) If any signal is generated by an asynchronous signal handler, the behavior is undefined.
The references to quick_exit() are new in C2011; they were not present in C1999.
POSIX 2008
The section on Signal Concepts goes through what is and is not allowed inside a signal handler under POSIX in considerable detail.
First, your signal handler is not exactly async signal safe. In practice this is probably not a problem in your case, since I assume the main() is basically doing nothing while it is waiting for the signal. But it is definately not correct anyway.
As for why the program exits, not counting segfault:s in the signal handler due to invalid use of FILE* functions such as printf, sscanf etc, when the signal is received any system calls you are doing (or, well, most) will be interreupted with EAGAIN.
If you are using something like sleep() in main to wait for the signal to occur it will be interrupted. You are expected to restart it manually.
To avoid this you probably want to use the significantly more portable sigaction interface instead of signal. If nothing else this allows you to indicate that you want system calls to be restarted.
The reason that FILE * functions (and most other functions that use global state such as malloc and free) is not allowed in signal handlers is that you might be in the middle of another operation on the same state when the signal arrives.
This can cause segfaults or other undefined operations.
The normal 'trick' to implement this is to have a self-pipe: The signal handler will write a byte to the pipe, and your main loop will see this (usually by waiting in poll or something similar) and then act on it.
If you absolutely want to do user interaction in the signal handler you have to use write() and read(), not the FILE* functions.
Here is my code,
#include<signal.h>
#include<stdio.h>
int main(int argc,char ** argv)
{
char *p=NULL;
signal(SIGSEGV,SIG_IGN); //Ignoring the Signal
printf("%d",*p);
printf("Stack Overflow"); //This has to be printed. Right?
return 0;
}
While executing the code, i'm getting segmentation fault. I ignored the signal using SIG_IGN. So I shouldn't get Segmentation fault. Right? Then, the printf() statement after printing '*p' value must executed too. Right?
Your code is ignoring SIGSEGV instead of catching it. Recall that the instruction that triggered the signal is restarted after handling the signal. In your case, handling the signal didn't change anything so the next time round the offending instruction is tried, it fails the same way.
If you intend to catch the signal change this
signal(SIGSEGV, SIG_IGN);
to this
signal(SIGSEGV, sighandler);
You should probably also use sigaction() instead of signal(). See relevant man pages.
In your case the offending instruction is the one which tries to dereference the NULL pointer.
printf("%d", *p);
What follows is entirely dependent on your platform.
You can use gdb to establish what particular assembly instruction triggers the signal. If your platform is anything like mine, you'll find the instruction is
movl (%rax), %esi
with rax register holding value 0, i.e. NULL. One (non-portable!) way to fix this in your signal handler is to use the third argument signal your handler gets, i.e. the user context. Here is an example:
#include <signal.h>
#include <stdio.h>
#define __USE_GNU
#include <ucontext.h>
int *p = NULL;
int n = 100;
void sighandler(int signo, siginfo_t *si, ucontext_t* context)
{
printf("Handler executed for signal %d\n", signo);
context->uc_mcontext.gregs[REG_RAX] = &n;
}
int main(int argc,char ** argv)
{
signal(SIGSEGV, sighandler);
printf("%d\n", *p); // ... movl (%rax), %esi ...
return 0;
}
This program displays:
Handler executed for signal 11
100
It first causes the handler to be executed by attempting to dereference a NULL address. Then the handler fixes the issue by setting rax to the address of variable n. Once the handler returns the system retries the offending instruction and this time succeeds. printf() receives 100 as its second argument.
I strongly recommend against using such non-portable solutions in your programs, though.
You can ignore the signal but you have to do something about it. I believe what you are doing in the code posted (ignoring SIGSEGV via SIG_IGN) won't work at all for reasons which will become obvious after reading the bold bullet.
When you do something that causes the kernel to send you a SIGSEGV:
If you don't have a signal handler, the kernel kills the process and that's that
If you do have a signal handler
Your handler gets called
The kernel restarts the offending operation
So if you don't do anything abut it, it will just loop continuously. If you do catch SIGSEGV and you don't exit, thereby interfering with the normal flow, you must:
fix things such that the offending operation doesn't restart or
fix the memory layout such that what was offending will be ok on the
next run
Another option is to bracket the risky operation with setjmp/longjmp, i.e.
#include <setjmp.h>
#include <signal.h>
static jmp_buf jbuf;
static void catch_segv()
{
longjmp(jbuf, 1);
}
int main()
{
int *p = NULL;
signal(SIGSEGV, catch_segv);
if (setjmp(jbuf) == 0) {
printf("%d\n", *p);
} else {
printf("Ouch! I crashed!\n");
}
return 0;
}
The setjmp/longjmp pattern here is similar to a try/catch block. It's very risky though, and won't save you if your risky function overruns the stack, or allocates resources but crashes before they're freed. Better to check your pointers and not indirect through bad ones.
Trying to ignore or handle a SIGSEGV is the wrong approach. A SIGSEGV triggered by your program always indicates a bug. Either in your code or code you delegate to. Once you have a bug triggered, anything could happen. There is no reasonable "clean-up" or fix action the signal handler can perform, because it can not know where the signal was triggered or what action to perform. The best you can do is to let the program fail fast, so a programmer will have a chance to debug it when it is still in the immediate failure state, rather than have it (probably) fail later when the cause of the failure has been obscured. And you can cause the program to fail fast by not trying to ignore or handle the signal.