Chris Lutz
Chris Lutz

Reputation: 75439

Catching segfaults in C

I have a program that segfaults from pointer arithmetic sometimes. I know this happens, but I can't easily check ahead of time to see whether it segfaults or not - either I can "pre-scan" input data to see if it will cause a segfault (which can be impossible to determine), or I can refit it to not use pointer arithmetic, which would require a significantly larger amount of work, or I can try to catch a segfault. So my question:

1) How, in C, can I catch a segfault? I know something in the OS causes a segfault, but what can a C program do in the event that it segfaults to die a bit more gracefully than just Segmentation fault?

2) How portable is this?

I imagine this is a highly unportable behavior, so if you post any code to catch a segfault, please tell me what it works on. I'm on Mac OS X but I'd like my program to work on as many platforms as it can and I want to see what my options are.

And don't worry - basically all I want to do is print a more user-friendly error message and free some malloc()ed memory, and then die. I'm not planning on just ignoring all segfaults I get and plowing ahead.

Upvotes: 12

Views: 18053

Answers (4)

Scottie T
Scottie T

Reputation: 12225

You have to define a signal handler. This is done on Unix systems using the function sigaction. I've done this with the same code on Fedora 64- and 32-bit, and on Sun Solaris.

Upvotes: 10

Dale Hagglund
Dale Hagglund

Reputation: 16480

The safe actions in a signal handler are very limited. It's unsafe to call any library function not known to be re-entrant, which will exclude, for example, free() and printf(). Best practice is to set a variable and return, but this doesn't help you very much. It's also safe to use system calls such as write().

Note that in the two backtrace examples given here, the backtrace_symbols_fd() function will be safe because it uses the raw fd directly, but the call to fprintf() is incorrect, and should be replaced by a use of write().

Upvotes: 5

blabla999
blabla999

Reputation: 3200

signal handling is (relatively) portable across unix machines (this includes mac and linux). The big differences are in the exception detail, which is passed as argument to the signal handling routine. Sorrty, but you will probably need a bunch of #ifdefs for that, if you want to print more reasonable error messages (such as where and due to which address the fault happened) ...

ok, here is a code fragment for you to start with:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

Your task is to:

  • check what SIGARGS is (OS dependent, so use an ifdef)
  • see how to extract fault-address and pc from the exception information in sigArgs
  • print reasonable message
  • exit

in theory, you could even patch the pc in the signal handler (to after the faulting instruction), and proceed. However, typical signal handlers either exit() or to a longjmp() back into a save place in the main.

regards

Upvotes: 1

Igor
Igor

Reputation: 27268

You can use the function signal to install a new signal handler for the signal:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

Something like the following code:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}

Upvotes: 18

Related Questions