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.
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'm trying to handle some of the C signals in the program I'm writing to add some safety nets in case of accidental closure etc. I've been trying to set up a signal handler for SIGINT using sigaction, and I can see the handler is being called, but it's not stopping the program from being killed anyway.
Trying to use signal with SIG_IGN works fine, however stops me being able to use custom handling. I've tried using signal(...) with my handler, and sigaction(...), and neither are actually working.
void handler(int SIG) {
//I know this is unsafe just for testing.
printf("Handled signal %d!\n", SIG);
//No actual logic for now.
}
void initializer() {
//...
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
//This doesn't work.
sigaction(SIGINT, &sa, NULL);
//This also didn't work.
// signal(SIGINT, handler);
//...
}
I believed that handling the signal would stop it by default, and everything I've seen online has indicated this should be enough, but I still receive:
^CHandled signal 2!
And then the process is killed...
I have a program that needs to use one custom signal handler for SIGINT and one for SIGCHILD.
I therefore added two struct sigaction called sigchildStruct and sigintStruct, and used sigaction to define two custo signal handling functions: handleSigInt() and handleSigChild()
First of all, is this the way you are supposed to do it; needing to register two separate sigaction structs?
Second of all, i need to block SIGCHILD during part of the code execution, i only want to receive the signal at one place in the code, so i used:
sigdelset(&sigchildStruct.sa_mask,SIGCHLD);
// Catch SIGCHILD signal here
sigaddset(&sigchildStruct.sa_mask,SIGCHLD);
Is that how you would do that? More importantly: there are two sigaction structs, but do i only need to change the sa_mask on one of them, or on both? Now i only changed the sa_mask on the struct called sigchildStruct, and not on the one called sigintStruct.
The rest of the code:
void handleSigchild(int sig) {
int childPID,childExitStatus;
printf("\nSIGCHILD received\n");
while ((childPID = waitpid(-1,&childExitStatus,WNOHANG)) >0) {
if (childExitStatus==2) {printf("Background process: %d%s",childPID," terminated by SIGINT\n");}
else if (childExitStatus!=0) {printf("Background process: %d%s",childPID," unknown command\n");}
printf("Background process: %d%s\n",childPID," has exited");
}
}
void handleSigInt(int sig) {
// SIGINT will be sent to all child processes so nothing needs to be done
printf("\nSIGINT received\n");
}
int main(int argc, char ** argv) {
// Sigaction for SIGCHILD
struct sigaction sigchildStruct;
sigchildStruct.sa_handler = &handleSigchild;
sigemptyset(&sigchildStruct.sa_mask);
sigaddset(&sigchildStruct.sa_mask,SIGCHLD);
sigchildStruct.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sigchildStruct, 0) == -1) {
printf("Couldnt register signal handler: %s\n",strerror(errno));
exit(1);
}
// Sigaction for SIGINT
struct sigaction sigintStruct;
sigintStruct.sa_handler = &handleSigInt;
sigemptyset(&sigintStruct.sa_mask);
sigintStruct.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sigintStruct, 0) == -1) {
printf("Couldnt register signal handler: %s\n",strerror(errno));
exit(1);
}
sigdelset(&sigchildStruct.sa_mask,SIGCHLD);
// Catch SIGCHILD signal here
sigaddset(&sigchildStruct.sa_mask,SIGCHLD);
}
Because nobody answered, I'll try to do so. When I am dealing with signals I am using the function signal. This function takes two parameters: signal code and signal handler function. To start catching the signals I would put the code:
signal(SIGINT, handleSigInt);
signal(SIGCHILD, handleSigChild);
To stop catching the signal, I would execute:
signal(SIGINT, SIG_IGN);
signal(SIGCHILD, SIG_IGN);
Hope it helps.
There is several mistakes in your code.
1) It is not necessary to add SIGCHLD signal in the set of blocked signals during its handling. Every catched signal is blocked during its handling, so that there is no concurrent reentrance in the handling function :
struct sigaction sigchildStruct;
sigchildStruct.sa_handler = &handleSigchild;
sigemptyset(&sigchildStruct.sa_mask);
// sigaddset(&sigchildStruct.sa_mask,SIGCHLD); // unnecessary
sigchildStruct.sa_flags = SA_NOCLDSTOP;
2) You modified the sa_mask value of the struct after setting the action, which has no effect. If you want to block the delivery of signals for some time you need to modify the process signal mask. There is a function to do that : sigprocmask. So you can do something like :
sigset_t oldMask, newMask;
sigemptyset(&newMask);
sigaddset(&newMask,SIGCHLD);
sigprocmask(SIG_BLOCK,&newMask,&oldMask);
// now protected from SIGCHLD delivery, signal will be blocked...
sigprocmask(SIG_SETMASK,&oldMask,NULL);
// old protection reinstalled...
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
I've made the follow signal handler
struct sigaction pipeIn;
pipeIn.sa_handler = updateServer;
sigemptyset(&pipeIn.sa_mask);
sa.sa_flags = SA_RESTART;
if(sigaction(SIGUSR1, &pipeIn, NULL) == -1){
printf("We have a problem, sigaction is not working.\n");
perror("\n");
exit(1);
}
How do I remove or block this particular handler so that I can set up another signal handler that uses the same signal? Thanks.
Use SIG_DFL in place of the function pointer when calling sigaction(2).