Longjmp out of signal handler? - c

From the question:
Is it good programming practice to use setjmp and longjmp in C?
Two of the comments left said:
"You can't throw an exception in a signal handler, but you can do a
longjmp safely -- as long as you know what you are doing. – Dietrich
Epp Aug 31 at 19:57
#Dietrich: +1 to your comment. This is a little-known and
completely-under-appreciated fact. There are a number of problems that
cannot be solved (nasty race conditions) without using longjmp out of
signal handlers. Asynchronous interruption of blocking syscalls is the
classic example."
I was under the impression that signal handlers were called by the kernel when it encountered an exceptional condition (e.g. divide by 0). Also, that they're only called if you specifically register them.
This would seem to imply (to me) that they aren't called through your normal code.
Moving on with that thought... setjmp and longjmp as I understand them are for collapsing up the stack to a previous point and state. I don't understand how you can collapse up a stack when a signal handler is called since its called from the Kernel as a one-off circumstance rather than from your own code. What's the next thing up the stack from a signal handler!?

The way the kernel "calls" a signal handler is by interrupting the thread, saving the signal mask and processor state in a ucontext_t structure on the stack just beyond (below, on grows-down implementations) the interrupted code's stack pointer, and restarting execution at the address of the signal handler. The kernel does not need to keep track of any "this process is in a signal handler" state; that's entirely a consequence of the new call frame that was created.
If the interrupted thread was in the middle of a system call, the kernel will back out of the kernelspace code and adjust the return address to repeat the system call (if SA_RESTART is set for the signal and the system call is a restartable one) or put EINTR in the return code (if not restartable).
It should be noted that longjmp is async-signal-unsafe. This means it invokes undefined behavior if you call it from a signal handler if the signal interrupted another async-signal-unsafe function. But as long as the interrupted code is not using library functions, or only using library functions that are marked async-signal-safe, it's legal to call longjmp from a signal handler.
Finally, my answer is based on POSIX since the question is tagged unix. If the question were just about pure C, I suspect the answer is somewhat different, but signals are rather useless without POSIX anyway...

longjmp does not perform normal stack unwinding. Instead, the stack pointer is simply restored from the context saved by setjmp.
Here is an illustration on how this can bite you with non-async-safe critical parts in your code. It is advisable to e.g. mask the offending signal during critical code.

worth reading this: http://man7.org/linux/man-pages/man2/sigreturn.2.html in regard to how Linux handles signal handler invocation, and in this case how it manages signal handler exit, my reading of this suggests that executing a longjmp() from a signal handler (resulting in no call of sigreturn()) might be at best "undefined"... also have to take into account on which thread (and thus user stack) the setjmp() was called, and on which thread (and thus user stack) longjmp() in subsequently called also!

This doesn't answer the question of whether or not it is "good" to do this, but
this is how to do it. In my application, I have a complicated interaction between custom hardware, huge page, shared memory, NUMA lock memory, etc, and it is possible to have memory that seems to be decently allocated but when you touch it (write in this case), it throws a BUS error or SEGV fault in the middle of the application. I wanted to come up with a way of testing memory addresses to make sure that the shared memory wasn't node locked to a node that didn't have enough memory, so that the program would fail early with graceful error messages. So these signal handlers are ONLY used for this one piece of code (a small memcpy of 5 bytes) and not used to rescue the app while it is in use. I think it is safe here.
Apologies if this is not "correct". Please comment and I'll fix it up. I cobbled it together based on hints and some sample code that didn't work.
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
sigjmp_buf JumpBuffer;
void handler(int);
int count = 0;
int main(void)
{
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&(sa.sa_mask));
sigaddset(&(sa.sa_mask), SIGSEGV);
sigaction(SIGSEGV, &sa, NULL);
while (1) {
int r = sigsetjmp(JumpBuffer,1);
if (r == 0) {
printf("Ready for memcpy, count=%d\n",count);
usleep(1000000);
char buffer[10];
#if 1
char* dst = buffer; // this won't do bad
#else
char* dst = nullptr; // this will cause a segfault
#endif
memcpy(dst,"12345",5); // trigger seg fault here
longjmp(JumpBuffer,2);
}
else if (r == 1)
{
printf("SEGV. count %d\n",count);
}
else if (r == 2)
{
printf("No segv. count %d\n",count);
}
}
return 0;
}
void handler(int sig)
{
count++;
siglongjmp(JumpBuffer, 1);
}
References
https://linux.die.net/man/3/sigsetjmp
https://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html
http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/sig-1.html
https://www.gnu.org/software/libc/manual/html_node/Longjmp-in-Handler.html

In most systems a signal handler has it's own stack, separate from the main stack. That's why you could longjmp out of a handler. I think it's not a wise thing to do though.

You can't use longjmp to get out of a signal handler.
The reason for this is that setjmp only saves the resources (process registers) etc. that the calling-convention specifies that should be saved over a plain function call.
When an interrupt occurs, the function being interrupted may have a much larger state, and it will not be restored correctly by longjmp.

Related

How do I ensure the `SIGINT` signal handler is called as many times as `Ctrl+C` is pressed (with `longjmp`)?

Setup
In the code below, which simply prints some text until it times out, I added a handler (onintr()) for SIGINT. The handler onintr() does the following:
Resets itself as the default handler.
Prints out some text.
Calls longjmp().
Issue
It seems only the first Ctrl+C is interpreted correctly.
After pressing Ctrl+C the first time the print statement in onintr() appears on the screen and execution returns to where setjmp() was called. However, subsequent Ctrl+C calls get ignored.
Moreover, resetting onintr() as the handler doesn't seem to make a difference. In other words, if I let SIG_DFL become the default handler after onintr() was called once, subsequent Ctrl+C-s get ignored just as when onintr() was the handler; and I can't terminate the program.
The code signal.c:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf sjbuf;
void onintr(int);
void
onintr(int i)
{
signal(SIGINT, onintr);
printf("\nInterrupt(%d)\n", i);
longjmp(sjbuf, 0);
}
int
main(int argc, char* argv[])
{
int sleep_t = 1;
int ctr = 0;
int timeout = 10;
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
setjmp(sjbuf);
printf("Starting loop...\n");
while (ctr < timeout) {
printf("Going to sleep for %d second(s)\n", sleep_t);
ctr++;
sleep(sleep_t);
}
printf("\n");
return 0;
}
Behavior
On Ubuntu 22.04 I get the following:
gomfy:signal$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
gomfy:signal$ gcc signal.c
gomfy:signal$
gomfy:signal$ ./a.out
Starting loop...
Going to sleep for 1 second(s)
Going to sleep for 1 second(s)
^C
Interrupt(2)
Starting loop...
Going to sleep for 1 second(s)
Going to sleep for 1 second(s)
^CGoing to sleep for 1 second(s)
^CGoing to sleep for 1 second(s)
^CGoing to sleep for 1 second(s)
^CGoing to sleep for 1 second(s)
^CGoing to sleep for 1 second(s)
Going to sleep for 1 second(s)
gomfy:signal$
As you can see the subsequent Ctrl+C-s (^C) get ignored. Only the first one seems to call the handler.
For starters, from signal-safety(7) we learn:
An async-signal-safe function is one that can be safely called from within a signal handler. Many functions are not async-signal-safe. In particular, nonreentrant functions are generally unsafe to call from a signal handler.
This raises two problems:
printf is NOT async-signal-safe, and
mixing signal handlers, longjmp(3), and unsafe functions leads to Undefined Behaviour, as described later in the NOTES section:
If a signal handler interrupts the execution of an unsafe function, and the handler terminates via a call to longjmp(3) or siglongjmp(3) and the program subsequently calls an unsafe function, then the behavior of the program is undefined.
This means if the delivery of SIGINT happens to interrupt a call to printf then the program can no longer be reliably reasoned about.
As a possible red herring, the generic Linux manual for sleep(3) makes the claim:
On Linux, sleep() is implemented via nanosleep(2).
Portability notes
On some systems, sleep() may be implemented using alarm(2) and SIGALRM (POSIX.1 permits this); mixing calls to alarm(2) and sleep() is a bad idea.
and then ambiguously states:
Using longjmp(3) from a signal handler or modifying the handling of SIGALRM while sleeping will cause undefined results.
It is not entirely clear if this is only in reference to the previously mentioned alarm-based sleeps, but seems likely. The POSIX manual page for sleep(3) would appear to clarify this by making a similar claim:
If a signal-catching function interrupts sleep() and calls siglongjmp() or longjmp() to restore an environment saved prior to the sleep() call, the action associated with the SIGALRM signal and the time at which a SIGALRM signal is scheduled to be generated are unspecified.
and nanosleep(2) states:
POSIX.1 explicitly specifies that [nanosleep] does not interact with signals
Suffice to say, it does not appear that sleep contributes to this problem, at least on Linux.
The Linux manual on signal(2) highlights its issues with portability, and encourages the use of sigaction(2).
An interesting note is:
If the disposition is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum. If invocation of the handler caused the signal to be blocked, then the signal is unblocked upon return from the handler.
The portability section later details the differences between System V (reset) and BSD (block) semantics, and notes that glibc 2+ uses BSD semantics by default (by wrapping around sigaction(2)).
So if we longjmp out of a handler, is the signal ever unblocked?
Linux's overview of signals, signal(7), ultimately clarifies things in a section labeled Execution of signal handlers, detailing a five step process.
The last part of step one involves:
Any signals specified in act->sa_mask when registering the handler with sigprocmask(2) are added to the thread's signal mask. The signal being delivered is also added to the signal mask, unless SA_NODEFER was specified when registering the handler. These signals are thus blocked while the handler executes.
While step four and five are:
When the signal handler returns, control passes to the signal trampoline code.
The signal trampoline calls sigreturn(2), a system call that uses the information in the stack frame created in step 1 to restore the thread to its state before the signal handler was called. The thread's signal mask and alternate signal stack settings are restored as part of this procedure. Upon completion of the call to sigreturn(2), the kernel transfers control back to user space, and the thread recommences execution at the point where it was interrupted by the signal handler.
Again, what happens if we longjmp out of a handler? Here is the most important piece of information:
Note that if the signal handler does not return (e.g., control is transferred out of the handler using siglongjmp(3), or the handler executes a new program with execve(2)), then the final step is not performed. In particular, in such scenarios it is the programmer's responsibility to restore the state of the signal mask (using sigprocmask(2)), if it is desired to unblock the signals that were blocked on entry to the signal handler. (Note that siglongjmp(3) may or may not restore the signal mask, depending on the savesigs value that was specified in the corresponding call to sigsetjmp(3).)
So the answer is that, by jumping out of the signal handler, the signal mask retains SIGINT, and blocks delivery of subsequent signals. The manual mentions the use of sigprocmask(2) or sigsetjmp(3) and siglongjmp(3) to solve this problem.
Here is a simple example of using the latter. sigsetjmp needs a non-zero value as its second argument, which tells the pair of functions to save and restore the signal mask. siglongjmp simply replaces longjmp, and sigjmp_buf replaces jmpbuf.
signal is moved after sigsetjmp to avoid Undefined Behaviour in the event SIGINT is delivered before sigsetjmp executes, which would cause siglongjmp to operate on a garbage sigjmp_buf.
Additionally, ctr must be declared as volatile, as otherwise its value is unspecified. From setjmp(3) (applies to sigsetjmp as well):
[...] the values of automatic variables are unspecified after a call to longjmp() if they meet all the following criteria:
they are local to the function that made the corresponding setjmp() call;
their values are changed between the calls to setjmp() and longjmp(); and
they are not declared as volatile.
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
sigjmp_buf sjbuf;
void dump(const char *s)
{
write(STDOUT_FILENO, s, strlen(s));
}
void onintr(int i)
{
dump("SIGINT caught. Jumping away...\n");
siglongjmp(sjbuf, 42);
}
int main(void)
{
volatile int ctr = 0;
int timeout = 10;
if (0 != sigsetjmp(sjbuf, 1))
dump("Stuck the landing!\n");
else
signal(SIGINT, onintr);
dump("Starting loop...\n");
while (ctr < timeout) {
dump("Going to sleep...\n");
ctr++;
sleep(1);
}
dump("\n");
}
Starting loop...
Going to sleep...
^CSIGINT caught. Jumping away...
Stuck the landing!
Starting loop...
Going to sleep...
^CSIGINT caught. Jumping away...
Stuck the landing!
Starting loop...
Going to sleep...
Going to sleep...
Going to sleep...
^CSIGINT caught. Jumping away...
Stuck the landing!
Starting loop...
Going to sleep...
Going to sleep...
Going to sleep...
^CSIGINT caught. Jumping away...
Stuck the landing!
Starting loop...
Going to sleep...
Going to sleep...
If this still does not work, you may need to define a macro: _BSD_SOURCE on glibc 2.19 and earlier or _DEFAULT_SOURCE in glibc 2.19 and later. See: feature_test_macros(7).

How to make putenv to a reentrant function?

The function putenv is not a thread safe function, so I guess if I call pthread_mutex_lock before calling putenv, can I make putenv "thread safe" in this way?
I tried it but when I run it, segmentation fault came out.
Here is the code:
#include "apue.h"
#include <pthread.h>
pthread_mutex_t envlock = PTHREAD_MUTEX_INITIALIZER;
void thread_func(void*arg){
pthread_mutex_lock(&envlock);
char env[100];
sprintf(env,"hhh=%s",(char*)arg);
putenv(env);
pthread_mutex_unlock(&envlock);
return;
}
int main(){
pthread_t thread0, thread1, thread2;
void *shit;
int err;
char name0[]="thread0";
err=pthread_create(&thread0,NULL,thread_func,(void*)name0);
if(err!=0)
exit(-1);
char name1[]="thread1";
err=pthread_create(&thread1,NULL,thread_func,(void*)name1);
if(err!=0)
exit(-1);
char name2[]="thread2";
err=pthread_create(&thread2,NULL,thread_func,(void*)name2);
if(err!=0)
exit(-1);
pthread_join(thread0,&shit);
pthread_join(thread1,&shit);
pthread_join(thread2,&shit);
char *hhh=getenv("hhh");
printf("hhh is =%s",hhh);
return 0;
}
putenv is reentrant in newer versions of glibc. The problem is that putenv does not copy the string that is given to it, and therefore you cannot base it on your stack. Try keeping your char env[100] in a place where it will not be destroyed at the function's end.
The putenv() function is not required to be reentrant, and the one in
glibc 2.0 is not, but the glibc 2.1 version is.
...
Since version 2.1.2, the glibc implementation conforms to SUSv2: the
pointer string given to putenv() is used. In particular, this string
becomes part of the environment; changing it later will change the
environment. (Thus, it is an error to call putenv() with an
automatic variable as the argument, then return from the calling
function while string is still part of the environment.)
In general, protecting a function with a locking mechanism does not make it automatically reentrant. Reentrancy means precisely that, that a function can be reentered again, in the middle of a call to itself, without any risk to the internal data it manages int the active call. For that to occurr, the function must operate only on its stack frame, or be given pointers as parameters (in the stack, or in storable registers) to any externa data objects it must act upon. This makes reentrancy possible.
Now, I'll explain one scenario where using a non-reentrant function (with the locking mechanism you propose) is not applicable:
Assume the case you have a function f() that is being executed, then a signal is received (or an interrupt) and the signal handler just has to make a call to f(). As far as you have the function entry locked, the signal handler will be locked on entry to the function f(), making the handler never return, so the main program cannot continue its execution of f() to open the lock. In cases (the majority) you use the same stack to handle interrupts than the one of the interrupted function (well, I know about FreeBSD using a different context for interrupt handlers, but don't know if this applies to user mode processes), no chance to unlock the lock until the handler has return, but it cannot return as far as the handler is waiting for the lock to be unlocked. This case of reentrancy is not handled by your routine.
How can this problem be avoided. Just avoid interrupts that call this handler when you are in the middle of the shared region (then, why to lock it?) so interrupts are handled after the function call.
Of course, if you need to call it from several threads (each with it's own stack) then you need the lock after all.
Conclusion:
The lock just avoids the reentering of f(), but doesn't make it reentrant.

When I catch a signal with signal(SIGINT,f), is f executed in parallel?

I have C code like this
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler_function(int);
int i=0;
int j=0;
int main() {
signal(SIGINT,f);
while(1) {
/* do something in variable `i` */
}
}
void f(int signum) {
/* do something else on variable `i` */
}
Can it produce a data race? i.e. is f executed in parallel (even in a multithread machine) to the main. Or maybe is the main stopped until f finish its execution?
First of all according to the man page of signal() you should not use signal() but sigaction()
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.
But one might hope that signal() behaves sanely. However, there might be a data race because main might be interrupted before a store e.g. in a situation like this
if ( i > 10 ) {
i += j;
}
void f(int signum) {
i = 0;
}
If main is past the compare (or if the according registers do not get update if main was interrupted while compare), main would still to i += j which is a data race.
So where does this leave us? - Don't ever modify globals that get modified elsewhere in signal handlers if you cannot guarantee that the signal handler cannot interrupt this operation (e.g. disable signal handler for certain operations).
Unless you use the raise() from Standard C or kill() with the value from getpid() as the PID argument, signal events are asynchronous.
In single-threaded code on a multi-core machine, it means that you cannot tell what is happening in the 'do something to variable i' code. For example, that code might have just fetched the value from i and have incremented it, but not yet saved the incremented value. If the signal handler function f() reads i, modifies it in a different way, saves the result and returns, the original code may now write the incremented value of i instead of using the value modified by f().
This is what leads to the many constraints on what you can do in a signal handler. For example, it is not safe to call printf() in a signal handler because it might need to do memory allocation (malloc()) and yet the signal might have arrived while malloc() was modifying its linked lists of available memory. The second call to malloc() might get thoroughly confused.
So, even in a single-threaded program, you have to be aware and very careful about how you modify global variables.
However, in a single-threaded program, there will be no activity from the main loop while the signal is being handled. Indeed, even in a multi-threaded program, the thread that receives (handles) the signal is suspended while the signal handler is running, but other threads are not suspeded so there could be concurrent activity from other threads. If it matters, make sure the access to the variables is properly serialized.
See also:
What is the difference between sigaction() and signal()?
Signal concepts.

Catching Segmentation Violations and Getting on with Life

I'm writing a program that's examining its own address space.
Specifically, I care about all malloc-ed data blocks. If there some system call to get a list of them, that would be fantastic (for my application, I cannot use LD_PRELOAD, -wrap, nor any extra command line options). If there's a way to do this, I'd love to hear it even more than an answer to my stated problem, below.
In lieu of this, my current approach is to just dereference everything and look around. Obviously, the set of all possible pointers is a minefield of segfaults waiting to happen, so I tried registering a signal handler and using setjmp/longjmp (simply ignoring the segfault by making the handler do nothing is an infinite loop because the handler will return to the faulting instruction). Some example code goes like so:
static jmp_buf buf;
void handler(int i) {
printf(" Segfaulted!\n");
longjmp(buf,-1);
}
void segfault(void) {
int* x = 0x0;
int y = *x;
}
void test_function(void) {
signal(11,handler);
while (1) {
if (setjmp(buf)==0) {
printf("Segfaulting:\n");
segfault();
}
else {
printf("Recovered and not segfaulting!\n");
}
printf("\n");
}
}
The output is:
Segfaulting:
Segfaulted!
Recovered and not segfaulting!
Segfaulting:
Segmentation fault
So, the handler didn't work the second time around. I don't know why this is, but I speculated it had something to do with not clearing the original signal. I don't know how to do that.
As an aside, I tried sigsetjmp/siglongjmp first, but they weren't defined for some reason in setjmp.h. I got vague vibes that one needed to pass some extra compile flags, but, as before, that is not allowed for this application.
The system being used is Ubuntu Linux 10.04 x86-64, and any solution does not need to be portable.
[EDIT: sigrelse in the handler clears the signal, and fixes the problem effectively. Question now concerns the other issues raised--is there a better way (i.e., get the blocks of malloc)? What's up with sigsetjmp/siglongjmp? Why do I need to reset the signal?]
signal() is a legacy interface, and may or may not re-register a signal handler after it has been invoked, depending on the OS; you may need to issue another signal() call to reset the signal handler as the last action in your handler. See man 2 signal.
sigaction() is the preferred mechanism to set signal handlers, as it has well defined and portable behavior.
When the signal handler for SIGSEGV is invoked, the SIGSEGV signal will be masked as if by sigprocmask. This is true for any signal. Normally returning from the signal handler would unmask it, but since you're not returning, that never happens. There are a couple possible solutions:
You can call sigprocmask either before or after the longjmp to unmask it yourself.
You can install the signal handler with sigaction (the preferred way to do it anyway) and use the SA_NODEFER flag to prevent it from being masked.
You can use the sigsetjmp and siglongjmp functions, which themselves take responsibility for saving and restoring the signal mask.

Segmentation fault handling

I have an application which I use to catch any segmentation fault or ctrl-c.
Using the below code, I am able to catch the segmentation fault but the handler is being called again and again. How can I stop them.
For your information, I don't want to exit my application. I just can take care to free all the corrupted buffers.
Is it possible?
void SignalInit(void )
{
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = mysighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
sigaction(SIGSEGV, &sigIntHandler, NULL);
}
and handler goes like this.
void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}
Here for Segmentation fault signal, handler is being called multiple times and as obvious MyfreeBuffers() gives me errors for freeing already freed memory. I just want to free only once but still dont want to exit application.
Please help.
The default action for things like SIGSEGV is to terminate your process but as you've installed a handler for it, it'll call your handler overriding the default behavior. But the problem is segfaulting instruction may be retried after your handler finishes and if you haven't taken measures to fix the first seg fault, the retried instruction will again fault and it goes on and on.
So first spot the instruction that resulted in SIGSEGV and try to fix it (you can call something like backtrace() in the handler and see for yourself what went wrong)
Also, the POSIX standard says that,
The behavior of a process is undefined after it returns normally from
a signal-catching function for a [XSI] SIGBUS, SIGFPE, SIGILL, or
SIGSEGV signal that was not generated by kill(), [RTS] sigqueue(),
or raise().
So, the ideal thing to do is to fix your segfault in the first place. Handler for segfault is not meant to bypass the underlying error condition
So the best suggestion would be- Don't catch the SIGSEGV. Let it dump core. Analyze the core. Fix the invalid memory reference and there you go!
I do not agree at all with the statement "Don't catch the SIGSEGV".
That's a pretty good pratice to deal with unexpected conditions. And that's much cleaner to cope with NULL pointers (as given by malloc failures) with signal mechanism associated to setjmp/longjmp, than to distribute error condition management all along your code.
Note however that if you use ''sigaction'' on SEGV, you must not forget to say SA_NODEFER in sa_flags - or find another way to deal with the fact SEGV will trigger your handler just once.
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
static void do_segv()
{
int *segv;
segv = 0; /* malloc(a_huge_amount); */
*segv = 1;
}
sigjmp_buf point;
static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
{
longjmp(point, 1);
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sigaction));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NODEFER;
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, NULL); /* ignore whether it works or not */
if (setjmp(point) == 0)
do_segv();
else
fprintf(stderr, "rather unexpected error\n");
return 0;
}
If the SIGSEGV fires again, the obvious conclusion is that the call to MyfreeBuffers(); has not fixed the underlying problem (and if that function really does only free() some allocated memory, I'm not sure why you would think it would).
Roughly, a SIGSEGV fires when an attempt is made to access an inaccessible memory address. If you are not going to exit the application, you need to either make that memory address accessible, or change the execution path with longjmp().
You shouldn't try to continue after SIG_SEGV. It basically means that the environment of your application is corrupted in some way. It could be that you have just dereferenced a null pointer, or it could be that some bug has caused your program to corrupt its stack or the heap or some pointer variable, you just don't know. The only safe thing to do is terminate the program.
It's perfectly legitimate to handle control-C. Lots of applications do it, but you have to be really careful exactly what you do in your signal handler. You can't call any function that's not re-entrant. So that means if your MyFreeBuffers() calls the stdlib free() function, you are probably screwed. If the user hits control-C while the program is in the middle of malloc() or free() and thus half way through manipulating the data structures they use to track heap allocations, you will almost certainly corrupt the heap if you call malloc() or free() in the signal handler.
About the only safe thing you can do in a signal handler is set a flag to say you caught the signal. Your app can then poll the flag at intervals to decide if it needs to perform some action.
Well you could set a state variable and only free memory if its not set. The signal handler will be called everytime, you can't control that AFAIK.
I can see at case for recovering from a SIG_SEGV, if your handling events in a loop and one of these events causes a Segmentation Violation then you would only want to skip over this event, continue processing the remaining events. In my eyes SIG_SEGV is similar to the NullPointerException in Java. Yes the state will be inconsistent and unknown after either of these, however in some cases you would like to handle the situation and carry on. For instance in Algo trading you would pause the execution of an order and allow a trader to manually take over, with out crashing the entire system and ruining all other orders.
Looks like at least under Linux using the trick with -fnon-call-exceptions option can be the solution. It will give an ability to convert the signal to general C++ exception and handle it by general way.
Look the linux3/gcc46: "-fnon-call-exceptions", which signals are trapping instructions? for example.

Resources