2

I was playing with inline assembly, and I've noticed something strange. I've written a program which calls a wrapper function of jmp and executes in loop:

#include <stdint.h>

void asm_jmp(void* address)
{
    __asm__("jmp\t*%%rax"
            :
            :"a" (address)
            :);
}

int main()
{
    asm_jmp(&main + 4);
}

I've opened it with gdb and I noticed it iterates hundreds and hundreds of times before giving segmentation fault. Maybe I'm missing something, but I don't see where there could be a problem in this program which causes it to segfault.

Initially I thought that calling asm_jmp in loop saturated the stack, since each call adds an address onto the stack, but there is no return to free the space occupied by that address. Is this the problem? Or there's something else?

Here is the assembly obtained with objdump:

0000000000001119 <asm_jmp>:
    1119:       55                      push   %rbp
    111a:       48 89 e5                mov    %rsp,%rbp
    111d:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
    1121:       48 8b 45 f8             mov    -0x8(%rbp),%rax
    1125:       ff e0                   jmp    *%rax
    1127:       90                      nop
    1128:       5d                      pop    %rbp
    1129:       c3                      ret    

000000000000112a <main>:
    112a:       55                      push   %rbp
    112b:       48 89 e5                mov    %rsp,%rbp
    112e:       48 8d 05 0d 00 00 00    lea    0xd(%rip),%rax        # 1142 <main+0x18>
    1135:       48 89 c7                mov    %rax,%rdi
    1138:       e8 dc ff ff ff          call   1119 <asm_jmp>
    113d:       b8 00 00 00 00          mov    $0x0,%eax
    1142:       5d                      pop    %rbp
    1143:       c3                      ret    
    1144:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    114b:       00 00 00 
    114e:       66 90                   xchg   %ax,%ax
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Please [edit] your question and post the assembly code generated for this so what happens can be understood. – fuz Apr 10 '21 at 21:03
  • *but there is no return to free the space occupied by that address* - yes, exactly, so you've probably created a loop that pushes forever, until RSP points outside the region of memory the kernel is willing to let the stack grow into. (Between the `call` and the `push %rbp` in `asm_jmp`). – Peter Cordes Apr 10 '21 at 21:06
  • Ok than, I was not sure of this, since the segfault was almost instantaneous. Thanks for the answer. – Giovanni Zaccaria Apr 10 '21 at 21:08
  • 1
    It's of course Undefined Behaviour in GNU C to `jmp` to some arbitrary place in the middle of a function (especially to *another* function), and also to `jmp` out of an inline asm statement at all unless you use `asm goto` or follow it with `__builtin_unreachable()`. So the exact behaviour just depends on the asm details and compiler choices, and it doesn't really matter how you got this asm, just the fact of what it does is what you should be looking at. – Peter Cordes Apr 10 '21 at 21:10
  • 1
    @GiovanniZaccaria: "almost instantaneous" - yeah, so are most things on a 4GHz CPU. `ulimit -s` on Linux is normally 8 MiB, and push+call each push 8 bytes. The loop you've created will run at about 1 iteration per maybe 3 cycles, thanks to branch prediction + speculative execution hiding the store/reload latency of `mov %rdi,-0x8(%rbp)`. So at about 16/3 ~= 5.33 bytes per clock cycle, it doesn't take a 4GHz CPU long to grow the stack by 8MiB. (Not even when it has to stop for a page-fault every few pages.) – Peter Cordes Apr 10 '21 at 21:13

0 Answers0