LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   SIGSEGV handler (segmentation fauld handler) (http://www.linuxquestions.org/questions/programming-9/sigsegv-handler-segmentation-fauld-handler-277790/)

myp 01-14-2005 02:15 PM

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.

wapcaplet 01-14-2005 04:45 PM

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.

Mara 01-14-2005 05:09 PM

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.

myp 01-14-2005 06:28 PM

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)

Hko 01-14-2005 08:09 PM

Quote:

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
Signals occur asynchronously. So I suppose the kernel saves all contents of the registers stuff on the stack before the handler is called, including the instruction pointer(?!) (is it called like that? I haven't touched assembly since Z80, 6802,..). This may be the reason the program continues trying to execute the segfaulting instruction again (reasoning/guessing here).

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.

gaurav1086 03-08-2011 12:11 PM

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.

wje_lq 03-08-2011 12:39 PM

Quote:

Originally Posted by myp (Post 1408323)
The point is to determine if a pointer is pointing to a valid address by using SIGSEGV signal.

My apologies for not noticing this question earlier. (It's been almost two months.) The shell script below fulfills your requirement. It doesn't use a SIGSEGV handler, but it does use the SIGSEGV signal, as you require. Notice that since on a fork the heap is copy-on-write, and since the child doesn't write to the heap, this shouldn't be too expensive. The script gives me this output:
Code:

first  pointer valid? 1
second pointer valid? 0

Here's the script:
Code:

rm -f ./1; cat > 1.c <<EOD; gcc -Wall -Werror 1.c -o 1; ./1
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
pointer_valid_p(void *something)
{
  int  the_status;

  char  dummy_data;

  pid_t the_child_1;
  pid_t the_child_2;

  the_child_1=fork();

  if(the_child_1<0)
  {
    fprintf(stderr,"fork fail\n");

    exit(1);
  }

  if(the_child_1==0)
  {
    dummy_data=*(char *)something;

    exit(0);
  }

  the_child_2=waitpid(the_child_1,&the_status,0);

  return the_status==0;
}

int
main(void)
{
  int something;

  printf("first  pointer valid? %d\n",pointer_valid_p(&something));
  printf("second pointer valid? %d\n",pointer_valid_p((int *)112233));
  return 0;
}
EOD


Nominal Animal 03-08-2011 12:40 PM

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.

gaurav1086 03-08-2011 03:17 PM

Quote:

Originally Posted by Hko (Post 1408837)
Signals occur asynchronously. So I suppose the kernel saves all contents of the registers stuff on the stack before the handler is called, including the instruction pointer(?!) (is it called like that? I haven't touched assembly since Z80, 6802,..). This may be the reason the program continues trying to execute the segfaulting instruction again (reasoning/guessing here).

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.

This condition is not called memory leak as in this case the stack overflows and not the heap. Memory leaks are normally accomplished(irony) by malloc calls without calling subsequent free() calls.

Thanks,
gaurav.


All times are GMT -5. The time now is 10:30 PM.