GDB cannot access memory ... why does it matter where it returns in mapped and executable memory?

I have the following short program that sets the return address on the stack to some value so that when it returns it will go to that address:

(gdb) disas main
Dump of assembler code for function main:
   0x000000000040051c <+0>:     push   rbp
   0x000000000040051d <+1>:     mov    rbp,rsp
   0x0000000000400520 <+4>:     mov    DWORD PTR [rbp-0x14],edi
   0x0000000000400523 <+7>:     mov    QWORD PTR [rbp-0x20],rsi
   0x0000000000400527 <+11>:    lea    rax,[rbp-0x1]
   0x000000000040052b <+15>:    add    rax,0x9
   0x000000000040052f <+19>:    mov    QWORD PTR [rax],0x400520
   0x0000000000400536 <+26>:    mov    eax,0x0
   0x000000000040053b <+31>:    pop    rbp
   0x000000000040053c <+32>:    ret    
End of assembler dump.

      

Why does it matter which address I entered in [rax]? (0x400520 in this example, i.e. main + 4)

When I try different addresses it may succeed or fail with the error: "Unable to access memory at address 0x8", here (with main + 4) it fails:

(gdb) b *0x000000000040053c
Breakpoint 1 at 0x40053c
(gdb) run 
Breakpoint 1, 0x000000000040053c in main ()
=> 0x000000000040053c <main+32>:        c3      ret    
(gdb) x/a $rsp
0x7fffffffdd18: 0x400520 <main+4>
(gdb) si
Cannot access memory at address 0x8
(gdb) x/i $rip
=> 0x400520 <main+4>:   mov    DWORD PTR [rbp-0x14],edi

      

What is the problem? The address it returns is displayed, readable, and executed. "si" only executes one command, so it only executes the ret instruction.

I can also see that the address the rip was set to contains a valid instruction to execute.

It might seem interesting to note that manually setting $ rip to the same address doesn't seem to cause problems (for example, just set $ rip = 0x400520 and then si).

Where does this attempt to access address 0x8 come from? Why such access at all and where does "0x8" come from? ret only pops off the stack into rip. Surely there is something obvious that I'm missing here?

If I go back to different addresses for example. main + 0 or main + 32, it works, but most of the others seem to fail.

The C program is simple (+9 magic just gotten with gdb, to see how much I need it might need to tweak another system):

#include <stdio.h>
#include <stdint.h>

int main(int argc, char **argv)
{
        char stack; 

        *((uint64_t *) (&stack + 9)) = 0x0000000000400520;

        return 0;
}

      

I am using GNU gdb (Gentoo 7.6.2 p1) 7.6.2, but I also noticed an issue with GNU gdb (GDB) 7.5-4.0.61. I am using gcc (Gentoo 4.7.3-r1 p1.4, pie-0.5.5) 4.7.3.

+3


source to share





All Articles