I am trying to handle SIGSEGV signal from my code. I have written the following code in C under Fedora 15. My problem is that the signal handler is not getting called for the second segmentation fault. Can anybody kindly point out what i am doing wrong.
typedef struct _ST_DEMO
{
int m_iUnused;
_ST_DEMO()
{
m_iUnused = 0;
}
} ST_DEMO;
jmp_buf ex_buf__;
static void sig_hdl (int sig, siginfo_t *siginfo, void *context)
{
cout<<"Inside Signal Handler."<<endl;
longjmp(ex_buf__, 1);
}
int main (int argc, char *argv[])
{
ST_DEMO* pStDemo = 0;
struct sigaction act;
memset (&act, '\0', sizeof(act));
/* Use the sa_sigaction field because the handles has two additional parameters */
act.sa_sigaction = &sig_hdl;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &act, NULL) < 0)
{
perror ("sigaction");
return 1;
}
if(!setjmp(ex_buf__))
{
cout<<"Before First Seg Fault."<<endl;
cout<<pStDemo->m_iUnused<<endl;
}
else
{
cout<<"After jump."<<endl;
}
cout<<"Before Second Seg Fault."<<endl;
cout<<pStDemo->m_iUnused<<endl;
while(1)
{
sleep(1);
}
return 0;
}
Your longjmp will cause you to jump to the location, but you will not have returned from the signal handler. This means that the signal is still blocked (this is the default behavior for signals, they are masked until you have returned from the signal handler).
You can fix this by indicating that you want the signal to occur again by clearing the signal mask in your handler before longjmp.
Use the SA_NODEFER flag in act.sa_flags to prevent it from being masked in the first place.
Use the siglongjmp/sigsetjmp functions, which saves the mask for you
Or
Call sigprocmask either before or after the longjmp to unmask it yourself.
A warning: This is a very dangerous thing to do (catch SIGSEGV, and then longjmp out of the signal handler) and it will be almost impossible to do anything useful with it.
If the memory access error occurs in any function that is not async signal safe and reentrant you will not be able to continue in any kind of sane way anyway.
But since there are multiple similar questions on the site I guess this is some kind of exercise.
Related question:
Catching Segmentation Violations and Getting on with Life
Also useful
Longjmp out of signal handler?
longjmp() from signal handler
Related
jmp_buf functjmp;
void sigsegv_handler(int sig) {
sio_printf("Caught sigsegv!\n");
siglongjmp(functjmp, 2);
return;
}
void foo(unsigned val) {
assert(0);
sio_printf("entered!\n");
}
int main() {
struct sigaction action;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
sigaddset(&action.sa_mask, SIGSEGV);
action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
sigset_t prev_mask;
sigprocmask(SIG_BLOCK, NULL, &prev_mask);
if (sigsetjmp(functjmp, 0) == 0) {
foo(*(unsigned *)0x8);
} {
sigprocmask(SIG_BLOCK, &prev_mask, NULL);
sio_printf("jump handled!\n");
foo(*(unsigned *)0x8);
}
sio_fprintf(stderr, "how did it come here?!\n");
}
I've been debugging this code using gdb, and I cannot figure out why the program will not handle the second SIGSEGV signal with my own handler, assuming no other signals were received or sent by the program? Any sio prefixed functions are async safe variants of the stdio counterparts.
Currently, I surmise it has to do with something I'm missing in my conception about returning from the signal handler, which longjmp doesn't do at all.
Short answer: normally not possible to resume after SIGSEGV for C program. You might get more mileage with C++.
Long Answer: See discussions in Coming back to life after Segmentation Violation
Assuming OK to take the risk of undefined behavior:
It is possible to re-enable SEGV. The core issue is that during signal handler, the code explicitly blocks the SEGV signal from being triggered (with the sigaddset). In addition, the default behavior (of signal handlers) is that during signal handling, the same signal processing will be deferred until the signal handler returns. In the OP code, the signal handler never returns (because of the siglongjmp)
Both issues can be addressed by changing the original code.
// Make sure all attributes are NULL.
struct sigaction action = {} ;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
// Not Needed:: sigaddset(&action.sa_mask, SIGSEGV);
// Add SA_NODEFER to disable the deferred processing of SIGSEGV.
action.sa_flags = SA_RESTART | SA_NODEFER ; /* Restart syscalls if possible */
// rest of code here
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
...
I have this assignment to do:
Write a function void myfunct(void(*f)(int sig)) which sets f as handler to all the possible signals.
I have two problems:
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Thank you.
I would personally instead iterate over a static list of signal numbers, and use preprocessor directives to detect which ones are supported (at compile time). For example:
#include <signal.h>
static const all_signals[] = {
#ifdef SIGHUP
SIGHUP, /* POSIX.1 */
#endif
#ifdef SIGQUIT
SIGQUIT, /* POSIX.1 */
#endif
#ifdef SIGTRAP
SIGTRAP, /* POSIX.1 */
#endif
#ifdef SIGIO
SIGIO, /* BSD/Linux */
#endif
/*
* Other signal names omitted for brevity
*/
/* C89/C99/C11 standard signals: */
SIGABRT,
SIGFPE,
SIGILL,
SIGINT,
SIGSEGV,
/* SIGTERM (C89/C99/C11) is also the terminating signal number */
SIGTERM
};
with SIGTERM being the last entry in the array for which a signal handler is installed:
struct sigaction act;
int i = 0;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = your_signal_handler;
act.sa_flags = 0;
do {
if (sigaction(all_signals[i], &act, NULL)) {
fprintf(stderr, "Cannot install signal %d handler: %s.\n", all_signals[i], strerror(errno));
exit(EXIT_FAILURE);
}
} while (all_signals[i++] != SIGTERM);
This way your code does not require POSIX etc. support to work, but does support POSIX signals if available at compile time.
You can check the Wikipedia Unix signal article and man 7 signal for known signal names.
You can also install the signal handler for POSIX realtime signals using
#if SIGRTMAX-0 > SIGRTMIN-0
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
if (sigaction(i, &act, NULL)) {
fprintf(stderr, "Cannot install realtime signal %d handler: %s.\n", i, strerror(errno));
exit(EXIT_FAILURE);
}
#endif
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Most implementation provide a constant such as NSIG (Glibc provides NSIG) or _NSIG (Linux provides _NSIG). So, you can loop through that constant and set the same signal handling function for all of them.
There's no POSIX defined value for "highest signal number". There's been a proposal in POSIX to add a macro NSIG_MAX.
{NSIG_MAX}
Maximum possible return value of sysconf(_SC_NSIG). See [cross-ref to XSH sysconf()]. The value of {NSIG_MAX} shall be no greater than the number of signals that the sigset_t type (see [cross-ref to ]) is capable of representing, ignoring any restrictions imposed by sigfillset() or sigaddset().
But it hasn't made it to POSIX yet (most probably it'll a part of the POSIX version - issue 8).
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
The parameter that the signal handling function takes doesn't matter when you are setting a signal disposition. It takes the signal number but that doesn't prevent you from using it as a handler for multiple signals.
But there are special cases you need to handle. Certain signals that can't caught or ignored (SIGKILL and SIGSTOP). There are other signals (SIGFPE, SIGILL and SIGSEGV) for which, while allowed to caught, the signal handler can't return to its caller (i.e. you need exit from the signal handler).
1. How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Seems like there isn't a standardized list you can iterate, but we actually don't need such a list for your task. Signal numbers are always positive and at most SIGRTMAX. In between 1 and SIGRTMAX there might be some numbers that do not correspond to any signal, but you can still try to set a handler for these. From the posix specification of sigaction(sig, ...) (emphasis mine):
RETURN VALUE
Upon successful completion, sigaction() shall return 0; otherwise, -1 shall be returned, errno shall be set to indicate the error, and no new signal-catching function shall be installed.
ERRORS
The sigaction() function shall fail if:
[EINVAL]
The sig argument is not a valid signal number or an attempt is made to catch a signal that cannot be caught or ignore a signal that cannot be ignored.
So it is perfectly fine to call sigaction(123, ...) where 123 is not a signal.
2. Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Why do you think it should have no parameters?
Again, from the posix specification of sigaction():
Member Type
Member Name
Description
void(*) (int)
sa_handler
Pointer to a signal-catching function
The signal handler is supposed to have an int parameter, just like your function f has, so there is no problem.
Putting everything together
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
struct sigaction siga;
void f(int sig) {
printf("Caught signal %d\n", sig);
}
// sets f as handler to all the possible signals.
void myfunct(void(*f)(int sig)) {
siga.sa_handler = f;
for (int sig = 1; sig <= SIGRTMAX; ++sig) {
// this might return -1 and set errno, but we don't care
sigaction(sig, &siga, NULL);
}
}
int main() {
myfunct(f);
pause(); // wait for signal
return 0;
}
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.
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;
}
This question already has answers here:
Catch Ctrl-C in C
(9 answers)
Closed 2 years ago.
I use the following code to catch Ctrl+C in my C program
Code
void sig_handler(int signo)
{
if (signo == SIGINT)
exit(EXIT_SUCCESS);
}
void main ()
{
......
if(signal(SIGINT, sig_handler)== SIG_ERR)
{
printf(">>>>>>>>>>>>>>>>>>>>> SIG INT EROOR !!!! sigint=%d ID=%d \n",SIGINT, getpid());
}
else
printf(">>>>>>>>>>AFTER>>>>>>>>>>> SIG INT sigint=%d PID=%d \n",SIGINT, getpid());
char *buf = NULL;
asprintf(&buf, "%d", getpid());
write(fd, buf, strlen(buf));
free(buf);
uloop_run(); //entering main loop
ubus_exit();
uloop_done();
xml_exit();
config_exit();
free(tmp);
closelog();
log_message(NAME, L_NOTICE, "exiting\n");
return 0;
}
My purpose is to catch Ctrl + C but it seem the signal handler function i.e sig_handler() doesn't run.
I want to know how to fix it?
As iharob answered, you should add the handler for the signal.
However, you should carefully read signal(7) and notice that it is not legal to call printf from inside a signal handler (since printf is not an async-signal-safe function). You should use write(2) instead of printf(3).
This restriction is significant and important. Don't forget that e.g. both printf and malloc could be interrupted at arbitrary moments, but they are not designed for that.
At the very least, call fflush(3) and/or end your printf format string with a \n; but that would still be undefined behavior (but you might be "unlucky" to have it do what you want most of the time).
BTW, it is recommended today to use sigaction(2) instead of the "obsolete" signal(2)
In practice, the recommended practice inside a signal handler would be most of the time to set some volatile sigatomic_t flag (to be tested outside the handler), or to call siglongjmp(3). If you insist on doing something else, be sure that you use (even indirectly) only async-signal-safe functions (and there are few of them, mostly the syscalls(2) ....). In particular, stdio(3) & malloc(3) should never be used from a signal handler (and that rules out most of the standard C functions, or most of library functions).
You may want to have some event loop around poll(2) (then you might be interested by the Linux specific signalfd(2)....); you should compile with all warnings and debug info (gcc -Wall -Wextra -g). Then use the gdb debugger (and also strace(1)) to debug your program.
Are you sure that the functions you are using (e.g. uloop_run, etc...) are not blocking or ignoring signals?. You should strace your program to find out!
You should add the handler to the signal with this function
sighandler_t signal(int signum, sighandler_t handler);
in your case
signal(SIGNINT, sig_handler);
One more thing, your main function must return int, so void main() is wrong, it should be int main().
The uloop_run function, from OpenWrt installs a signal handler for SIGINT so it's not possible to interrup it, and it overrides your signal handler.
That is the actual reason why your signal handler is never called.
The program wont handle the signal, until the uloop_run function exits, this is the uloop_run source with the relevant part
static void uloop_setup_signals(bool add)
{
struct sigaction s;
struct sigaction *act, *oldact;
memset(&s, 0, sizeof(struct sigaction));
if (add) {
s.sa_handler = uloop_handle_sigint;
s.sa_flags = 0;
act = &s;
oldact = &org_sighandler;
} else {
act = &org_sighandler;
oldact = NULL;
}
sigaction(SIGINT, act, oldact);
if (uloop_handle_sigchld) {
if (add) {
//act already points to s, so no need to update pointer
s.sa_handler = uloop_sigchld;
oldact = &org_sighandler_child;
} else {
act = &org_sighandler_child;
oldact = NULL;
}
sigaction(SIGCHLD, act, oldact);
}
}
void uloop_run(void)
{
struct timeval tv;
/*
* Handlers are only updated for the first call to uloop_run() (and restored
* when this call is done).
*/
if (!uloop_recursive_count++)
uloop_setup_signals(true);
while(!uloop_cancelled)
{
uloop_gettime(&tv);
uloop_gettime(&tv);
uloop_run_events(uloop_get_next_timeout(&tv));
}
if (!--uloop_recursive_count)
uloop_setup_signals(false);
}
as you can see, uloop_setup_signals(true); installs a new signal handler for SIGNINT and when the loop is finished uloop_setup_signals(false); is called restoring the previous signal handler.
So, this is the reason.
I am trying to catch a SIGSEGV from my program. I got a problem that my signal_handler doesn't catch the signal.
void handler(int sig){
printf("catch SIGSEGV");
exit(EXIT_FAILURE);
}
void foo(){
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_handler = handler;
if(sigaction(SIGSEGV, &sa, NULL) == -1){
handle_error("sigaction");
}
/* if SIGSEGV happen here, I can catch it */
bar();
}
void bar() {
/* if SIGSEGV happen here, I cannot catch it */
}
Is that means I have to install another signal handler inside bar?
But what if I have a bunch of function that want to catch the same signal. I have to install the signal handler for multiple times?
Update :
I tried to install the handler directly in the function but still cannot catch it. So I think it might be other problem. But that pretty weird. I use gdb to run and get
Program received signal SIGSEGV, Segmentation fault.
0x080499b1 in is_printable_string (
str=0xb80fe768 <Address 0xb80fe768 out of bounds>)
at trace/trace.c:259
259 while(str[index]!='\0'){
and this is my is_printable_String
int is_printable_string(char *str){
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if(sigaction(SIGSEGV, &sa, NULL) == -1){
handle_error("sigaction");
}
int index;
index=0;
while(str[index]!='\0'){
if(!isprint(str[index])){
return -1;
}
index++;
}
/* continue... */
This seems like I got a SEG fault, but I can't catch it
I intentionally passed that pointer, so nothing wrong with str parameter.
from the man page of sigaction ...
SA_SIGINFO (since Linux 2.2) The signal handler takes three arguments,
not one. In this case, sa_sigaction should be set instead of
sa_handler. This flag is only meaningful when establishing a signal
handler.
Therefore, your issue should be the line
sa.sa_flags = SA_SIGINFO;
Change it to
sa.sa_flags = 0;
and see how it goes.
Indeed as ajcaruana points out your handler doesn't match SA_SIGINFO. But if you are catching SIGSEGV you likely want SA_SIGINFO, to look at the offending address and stuff. In that case your signal handler needs to look like this:
static void
handler(int sig, siginfo_t *si, void *ucontext)
{
/* ... */
}
In particular, you might be interested in si_addr:
For the SIGBUS and SIGSEGV signals, this field contains the address
that caused the invalid memory reference.