Bad address after SIGSEGV?

Bad address after SIGSEGV?

Post by Thomas Koen » Wed, 25 Jan 1995 04:00:42



Suppose I've caught a SIGSEGV after a bad memory reference, and I want
to know what the memory address was.

How do I get at this?  If, as I strongly suspect, this can't be done,
I'd suggest adding it as a feature.

(The reason I'm asking this is that libckpt, a user-space checkpointing
library, relies on this information).
--

The joy of engineering is to find a straight line on a double
logarithmic diagram.

 
 
 

Bad address after SIGSEGV?

Post by Thomas Koen » Thu, 26 Jan 1995 10:02:05




Quote:>Suppose I've caught a SIGSEGV after a bad memory reference, and I want
>to know what the memory address was.

Following up on my own post, yes ;-)

I've since received information (thanks to Eugene Crosser,

signal handler sees; after the signal number, there's a
struct sigcontext_struct, which gives me the registers at the
time the signal happened.

However, if I look at the following program:

#include <sys/types.h>
#include <asm/signal.h>
#include <signal.h>
#include <stdio.h>

void _ckpt_pgfault_handler(int sig, struct sigcontext_struct dummy)
{
    printf("%d\n",dummy.cr2);
    return;

Quote:}

int main()
{
   char *a;
   char b;

   signal(SIGSEGV, _ckpt_pgfault_handler);
   a = (char *) 0xdeadbeef;
   b = *a;
   return 0;

Quote:}

with gdb, the only place where I find the 0xdeadbeef value is in eax
(where it was stored for a, in the main program).  I still don't know
where else to look for it.

And what's the cr2 register, anyway?
--

The joy of engineering is to find a straight line on a double
logarithmic diagram.

 
 
 

Bad address after SIGSEGV?

Post by Thomas Koen » Thu, 26 Jan 1995 23:26:42




[I wrote]

Quote:>> Suppose I've caught a SIGSEGV after a bad memory reference, and I want
>> to know what the memory address was.
>well int linux/arch/i386/mm/fault.c:
>        if (error_code & PAGE_USER) {
>                current->tss.cr2 = address;
>                current->tss.error_code = error_code;
>                current->tss.trap_no = 14;
>                send_sig(SIGSEGV, current, 1);
>                return;
>        }
>and arch/i386/kernel/signal.c
>        put_fs_long(current->tss.cr2, frame+23);

Problem is:  this doesn't work (at least not with 1.1.73 on a P90; I
don't run a much newer kernel because of the broken NCR driver).

$ cat dump.c
#include <sys/types.h>
#include <asm/signal.h>
#include <signal.h>
#include <stdio.h>

void _ckpt_pgfault_handler(int sig, struct sigcontext_struct context)
{
    printf("%d\n",context.cr2);
    return;

Quote:}

int main()
{
   char *a;
   char b;

   signal(SIGSEGV, _ckpt_pgfault_handler);
   a = (char *) 0xdeadbeef;
   b = *a;
   return 0;

Quote:}

$ cc -g dump.c
dump.c: In function `main':
dump.c:16: warning: passing arg 2 of `signal' from incompatible pointer type
$ ./a.out
0
Segmentation fault
$ gdb a.out
[...]
(gdb) b _ckpt_pgfault_handler
Breakpoint 1 at 0x67: file dump.c, line 8.
(gdb) r
Starting program: /home/ig25/test/a.out

Program received signal SIGSEGV, Segmentation fault.
0xb8 in main () at dump.c:18
18         b = *a;
(gdb) info registers
eax            0xdeadbeef       -559038737
ecx            0xbffffbd8       -1073742888
edx            0x0      0
ebx            0x0      0
esp            0xbffffbf8       0xbffffbf8
ebp            0xbffffc00       0xbffffc00
esi            0x52d60  339296
edi            0x0      0
eip            0xb8     0xb8
ps             0x10282  66178
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x2b     43
gs             0x2b     43
(gdb) set radix 16
Input and output radices now set to decimal 16, hex 10, octal 20.
(gdb) c
Continuing.

Breakpoint 1, _ckpt_pgfault_handler (sig=0xb, context={gs = 0x2b, __gsh = 0x0,
      fs = 0x2b, __fsh = 0x0, es = 0x2b, __esh = 0x0, ds = 0x2b, __dsh = 0x0,
      edi = 0x0, esi = 0x52d60, ebp = 0xbffffc00, esp = 0xbffffbf8, ebx = 0x0,
      edx = 0x0, ecx = 0xbffffbd8, eax = 0xdeadbeef, trapno = 0xd, err = 0x0,
      eip = 0xb8, cs = 0x23, __csh = 0x0, eflags = 0x10282,
      esp_at_signal = 0xbffffbf8, ss = 0x2b, __ssh = 0x0, i387 = 0x0,
      oldmask = 0x0, cr2 = 0x0}) at dump.c:8
8           printf("%d\n",context.cr2);
(gdb)

Quote:>so the sigcontext should just have it:

... and it doesn't, at least not in current->cr2.
--

The joy of engineering is to find a straight line on a double
logarithmic diagram.
 
 
 

Bad address after SIGSEGV?

Post by Thomas Koen » Sun, 29 Jan 1995 01:41:13


Ok, summary time.

The correct way to get the offending address in a signal handler
after a SIGSEGV ist the following (this assumes Linux on a i386
architecture):

#define __KERNEL__
#include <linux/signal.h>
#undef __KERNEL__

void sigsegv_handler(int sig, struct sigcontext_struct regs)
{
    void *offending_address;
    offending_address = (void *) regs.cr2;
    /* whatever */

Quote:}

This will work for bad memory references below 0xc0000000; above that
(in the memory the kernel reserves for itself) regs.cr2 will be
zero.

The thing that threw me was that 0xdeadbeef, the constant I chose,
was in kernel space.  Ouch.


me in the right direction.
--

The joy of engineering is to find a straight line on a double
logarithmic diagram.

 
 
 

Bad address after SIGSEGV?

Post by Steven Buytae » Tue, 31 Jan 1995 00:22:29




: >Suppose I've caught a SIGSEGV after a bad memory reference, and I want
: >to know what the memory address was.

: Following up on my own post, yes ;-)

: I've since received information (thanks to Eugene Crosser,

: signal handler sees; after the signal number, there's a
: struct sigcontext_struct, which gives me the registers at the
: time the signal happened.

: However, if I look at the following program:

: #include <sys/types.h>
: #include <asm/signal.h>
: #include <signal.h>
: #include <stdio.h>

: void _ckpt_pgfault_handler(int sig, struct sigcontext_struct dummy)
: {
:     printf("%d\n",dummy.cr2);
:     return;
: }
: int main()
: {
:    char *a;
:    char b;

:    signal(SIGSEGV, _ckpt_pgfault_handler);
:    a = (char *) 0xdeadbeef;
:    b = *a;
:    return 0;
: }

: with gdb, the only place where I find the 0xdeadbeef value is in eax
: (where it was stored for a, in the main program).  I still don't know
: where else to look for it.

  I have some comments on this code and the problems that you encounter.
  I can not give you a straight path to success though.

  Observations:

  1) if you include a trap for SIGSEGV, you better not do anything that
     requires memory manipulation. The only thing you can do is correct
     the situation if that is possible, like changing the protection of
     the faulted on page. Calling 'printf()' therefore could become
     dangerous. Read again 'could'...

  2) if my memory serves correctly (I don't have the kernel sources around),
     setting the cr2 register is only done in some cases. You should check
     out the complete flow of control of the do_page_fault function in
     ./mm/memory.c. Making your pointer point to 0xdeadbeef makes it fall
     outside the permissable range already.(?) I though that the maximum
     user memory range was 3Gb ? Anyhow, check these facts out first.

  3) You could check for wether your signal handler receives the correct
     address by protecting the page. I don't include sample code, but
     you could try to :

     - allocate some memory
     - align the allocated chunk to a page boundary
     - call mprotect() to protect (READ or READ/WRITE) that
       page
     - violate the protection by reading or writing to the chunk.

     This should give you a nice, clean SIGSEGV and a means to check that
     the cr2 register indeed contains the violated address. I know since I
     did it myself about a year ago...

  If you still have problems, try to locate the 'electric fence' utility
  from Bruce Perens (sp?) on sunsite. Try 'efence' with archie. This
  library is, besides useful for helping to plumb memory leaks,
  also a nice example of how to call mprotect() and catching the
  signal SIGSEGV.

  Hopes this helps a bit...

--
Steven Buytaert



        'Imagination is more important than knowledge.'
                        (A. Einstein)

 
 
 

1. SIGSEGV and bad addr

Hello,

Under linux 2.0, I have defined a handler to catch SIGSEGV (with signal()
function). But inside this handler I need to know the address (addr) that has
caused SIGSEGV. How should I do ? The man pages are not enough
detailed/clear.

Under sunos I define the handler like:

   static void SIGSEGV_Handler(int sig,int code,int scp,void *addr)

and under alpha:

   static void SIGSEGV_Handler(int sig,int code,struct sigcontext *scp)
   and addr=(void *) (scp->sc_traparg_a0)

what about linux ?

Thank you

--
Daniel Diaz
INRIA Rocquencourt - 78153 Le Chesnay Cedex
FRANCE               tel: +33 1 39 63 52 67

============================================

2. Curses and SCO 5

3. SIGSEGV and the bad addr

4. IPv6: Improvement of Source Address Selection

5. How I trace back SIGSEGV etc. (was: Re: SIGSEGV trace)

6. diald + RedHat 5.1 - Need config help

7. address for SIGSEGV cause -- HOW?

8. Unzip for Unix?

9. How to extract fault address inside SIGSEGV handler

10. How to extract fault address from inside SIGSEGV handler

11. SIGSEGV ? address of faulting mem reference

12. How to pass address to sigsegv handler

13. Is it possible to determine an address generating SIGSEGV?