LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 01-14-2005, 02:15 PM   #1
myp
LQ Newbie
 
Registered: Jan 2005
Posts: 2

Rep: Reputation: 0
Exclamation 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.
 
Old 01-14-2005, 04:45 PM   #2
wapcaplet
Guru
 
Registered: Feb 2003
Location: Colorado Springs, CO
Distribution: Gentoo
Posts: 2,018

Rep: Reputation: 48
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.
 
Old 01-14-2005, 05:09 PM   #3
Mara
Moderator
 
Registered: Feb 2002
Location: Grenoble
Distribution: Debian
Posts: 9,539

Rep: Reputation: 149Reputation: 149
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.
 
Old 01-14-2005, 06:28 PM   #4
myp
LQ Newbie
 
Registered: Jan 2005
Posts: 2

Original Poster
Rep: Reputation: 0
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)
 
Old 01-14-2005, 08:09 PM   #5
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: ubuntu
Posts: 2,530

Rep: Reputation: 108Reputation: 108
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.

Last edited by Hko; 01-14-2005 at 08:11 PM.
 
Old 03-08-2011, 12:11 PM   #6
gaurav1086
LQ Newbie
 
Registered: Dec 2008
Location: bangalore,india
Distribution: debian lenny
Posts: 13

Rep: Reputation: 0
Talking

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.
 
Old 03-08-2011, 12:39 PM   #7
wje_lq
Member
 
Registered: Sep 2007
Location: Mariposa
Distribution: Debian lenny, Slackware 12
Posts: 809

Rep: Reputation: 178Reputation: 178
Quote:
Originally Posted by myp View Post
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
 
Old 03-08-2011, 12:40 PM   #8
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942
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.
 
Old 03-08-2011, 03:17 PM   #9
gaurav1086
LQ Newbie
 
Registered: Dec 2008
Location: bangalore,india
Distribution: debian lenny
Posts: 13

Rep: Reputation: 0
Smile

Quote:
Originally Posted by Hko View Post
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.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
(linux) handler kpachopoulos Programming 2 11-10-2005 04:12 AM
Shutdown Handler Error f34r7h1s Linux - Newbie 2 04-26-2005 10:56 PM
<0>Kernel panic: Aiee, killing interrupt handler! In interrupt handler - not syncing mrb Linux - Newbie 2 01-09-2005 10:47 AM
how to define c terminate handler onnyloh Programming 3 08-28-2004 01:14 AM
Driver NMI Handler charlies Programming 0 03-14-2002 06:14 PM


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

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration