This question is asked with Linux in mind. GCC compiler is used.
What behaviour can be expected if SIGSEGV (I mean a violation that normally causes SIGSEGV) occurs within a signal handler whose purpose was to catch SIGSEGV? Code example to aid the discussion:
/* In main or whatever */
{
struct sigaction sa = {}; /* initialised to all zero (I vote for GCC style breach of standard here) */
sa.sa_handler = DisasterSignals;
sa.sa_flags = SA_RESETHAND | SA_NODEFER; /* To have or have not */
sigaction(SIGSEGV, &sa, NULL);
}
static void DisasterSignals(int signal)
{
/* We cannot save the situation, the purpose of catching the signal is
only to do something clever to aid debugging before we go. */
/* Q: What if we segfault in here?? */
abort(); /* This should give us the expected core dump (if we survive to this point) */
}
Imagine, in the point "Q", There is an offending machine instruction.
1) Without the SA_RESETHAND | SA_NODEFER
: This appears to put the system in a logical trap: At "Q", SIGSEGV should be generated. But SIGSEGV is blocked in the signal handler (default sigaction behaviour). How can the execution continue? Will it freeze? Will it jump past the offending instruction (I guess not)?
2) With the SA_RESETHAND | SA_NODEFER
: I guess in this case the program will crash in a "normal" fashion when SIGSEGV is repeated.
3) With only SA_NODEFER
: I guess in this case the signal handler is recursively called when SIGSEGV is repeated; if the SIGSEGV is always repeated, we get a freeze until the stack overflows, and then what.
You can ignore program error signals like SIGSEGV , but ignoring the error won't enable the program to continue executing meaningfully. Ignoring user requests such as SIGINT , SIGQUIT , and SIGTSTP is unfriendly.
SIGSEGV is triggered by the operating system, which detects that a process is carrying out a memory violation, and may terminate it as a result.
SIGSEGV is an MMU (virtual memory hardware) exception. SIGSEGV does not necessarily imply that any UB occurred, it only means you tried to access memory that isn't mapped. The reason you can't block it is because after the signal is raised the program retries the same operation that caused the problem.
The SIGSEGV signal is raised when you attempt to illegally access or modify memory. SIGSEGV is usually caused by using uninitialized or NULL pointer values or by memory overlays.
By default, while signal is being handled it is masked, so it can't be triggered recursively. If masked signal is triggered by program execution (invalid memory access, segfault, division by 0 etc.), the behavior is undefined:
If SIGBUS, SIGFPE, SIGILL, or SIGSEGV are generated while they are blocked, the result is undefined, unless the signal was generated by kill(2), sigqueue(3), or raise(3).
On my system, it causes process to crash.
With SA_NODEFER
there is no masking, so signal can be handled recursively until stack overflows. And adding SA_RESETHAND
would restore default action (crash for SIGSEGV).
I adapted your example to simple testing program, so you can verify this behavior:
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
volatile char *ptr;
static void DisasterSignals(int signal)
{
/* We cannot save the situation, the purpose of catching the signal is
only to do something clever to aid debugging before we go. */
write(1, "11\n", 3);
*ptr = 1;
write(1, "13\n", 3);
abort(); /* This should give us the expected core dump (if we survive to this point) */
}
struct sigaction sa = {}; /* initialised to all zero (I vote for GCC style breach of standard here) */
int main()
{
sa.sa_handler = DisasterSignals;
sa.sa_flags = /*SA_RESETHAND | */SA_NODEFER; /* To have or have not */
sigaction(SIGSEGV, &sa, NULL);
write(1, "25\n", 3);
*ptr = 1;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With