![]() |
SIGSEGV handler (segmentation fauld handler)
Hi All,
I am trying to write a handler for SIGSEGV signal. The point is to determine if a pointer is pointing to a valid address by using SIGSEGV signal. My code looks like this: typedef void handler_t(int); /* Signal: wrapper for sigaction */ handler_t * Signal(int sig, handler_t *h) { struct sigaction action, old_action; action.sa_handler = h; sigemptyset(&action.sa_mask); action.sa_flags = SA_RESTART | SA_NODEFER; if (sigaction(sig, &action, &old_action)<0) fprintf(stderr, "NOT GOOD!\n"); return old_action.sa_handler; } void handler(int sig) { printf("inside handler...\n"); return; } void main() { int *p = (int*)112233; int x; Signal(SIGSEGV, handler); x = *p; // access bad memory => SIGSEGV sent printf("got to here..."); } The problem is that either the handler is called infinitly many times, which means that I am getting lots of SIGSEGV signals instead of one. Or the program exists without getting to the printf function inside main. What is the problem here??? After system calls my handler it should continue executing the next instruction after x=*p right? Please help. Thanks in advance. |
I'm not certain, but I think segmentation faults are one thing that can't be "handled" from within your program. As soon as your program does an invalid memory reference, it's killed, and its exit status is SIGSEGV.
If I understand you correctly, you want some way to gracefully exit when a segmentation fault occurs. I don't know of a way to do so. The best way to avoid segmentation faults in C/C++ is to carefully plan out your memory allocation, usage, and deletion. If you are using a lot of pointers, walk through your code in your head (or on paper, for complex code), and be confident that you are not going to have invalid pointer references. Check (using assert, maybe) for NULL pointers before any pointer reference, even if you think there's no way it could ever be NULL. Be paranoid about checking the validity of your pointers before using them. |
SIGSEGV is handable. Only SIGKILL and SIGSTOP are not.
Have you tried the code without SA_RESTART? I'm not sure (manpage is not saying it clearly), but it may restart the bad call. |
Thank you guys for responding so quickly.
Removing SA_RESTART does not work. This flag just tells the OS to restart system calls if they are interrupted by this signal. The output of the program is still repeating copies of "inside handler..." which is printed inside handler. The control never reaches the printf statement inside main() function. Using gdb debugger I have been able to trace this to the fact that after my sighandler is called the control returns to the line x = *p, which again generates SIGSEGV and everything repeats forever. Using some assembly code I was able to make this work, but its a big hack. I wrote handler in assembly which doesnt return (doesnt call ret) but instead jumps to instruction after x = *p so just one SIGSEGV signal is generated. I have noticed that inside handler function, stack contains some weird stuff that OS has put there when the interrupt occured so before handler is invoked I save my ebp and esp registers in global variables so I can restore them after the jump from handler. This is probably bad (actually its horrible), because OS is waiting for handler to return and probably does some stuff after that since it fot so much info on the stack. But this works and so far I haven't encountered any problems. So here is some assembly for code check(void *p) returns 1 if p is valid and 0 otherwise. Also the handle to be passed to signal function with SIGSEGV is shown in GNU assembler: .global handler .global check # function which handles SIGSEGV signals handler: movl $0, _ptr # set global variable that address is bad jmp MARK # jumps after instruction which caused SIGSEGV # functions which returns if pointer is valid check: movl 4(%esp), %eax # get argument (void *p) movl %esp, _esp # save registers in global variables cause they get corrupted movl %ebp, _ebp movl $1, _ptr movl (%eax), %eax # do memory access, this may raise SIGSEGV MARK: movl _esp, %esp # restore stack movl _ebp, %ebp movl _ptr, %eax # return answer ret .comm _esp, 16 .comm _ebp, 16 .comm _ptr, 16 Of course to make this work, first call Signal(SIGSEGV, handler) |
Quote:
SIGSEGV can be handled: gdb does it. I don't have any experience with this, but I imagine catching segfaults is weird business. I think the segfault situation needs to be solved some way before the program will be able to continue. You probably already realize this,.. but when you do not execute the RET in the handler you'll have the stack grow each time the handler is called, you'll have a memory leak, eventually resulting in an out-of-memory condition. |
Hi,
I read only the first post. Please do not mind if my post is redundant. SIGSEGV is supposed to terminate the process instantly because the process may corrupt the data outside its addressble space. If you register a signal handler for SIGSEGV, then the control jumps to the signal_handler and executes. Meanwhile, the process is still faulty (and has not been punished) since it never terminated, So it again receives a SIGSEGV from the kernel upon which it again calls the signal_handler and this process continues infinitely. That is the reason you see what you see (;) Hope this gets clearer to you. Cheers, Gaurav. India. |
Quote:
Code:
first pointer valid? 1Code:
rm -f ./1; cat > 1.c <<EOD; gcc -Wall -Werror 1.c -o 1; ./1 |
Like the above posters have stated, your signal handler should by default halt the program (via e.g. an exit() call). If you don't, the same faulty access is retried over and over again, causing the infinite loop.
However, there is another way to handle the situation. Before installing the signal handler, you can call setjmp to save the execution context at that point. Your SIGSEGV signal handler then uses longjmp to restore that execution context. (You might want to use sigsetjmp and siglongjmp instead, though.) The first time setjmp is called, it will return 0. If SIGSEGV occurs, and longjmp/siglongjmp is called, the execution continues from where the setjmp returned, but this time the function will return a nonzero value (the one specified in the longjmp call). Hope this helps. |
Quote:
Thanks, gaurav. |
| All times are GMT -5. The time now is 04:27 PM. |