Copying existing function into memory buffer

193 views Asked by At

I'm trying to load a function into a mapped memory buffer and call it later so I've made a test case to try out:

auto func() -> void{
    asm(
        "nop;"
        "nop;"
        "nop;"
        "nop;"
    );
}

auto main(int argc, char *argv[]) -> int{
    void *exec_mem = mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    // check errors here

    memcpy(exec_mem, reinterpret_cast<const void*>(func), 5); // size is known
    (reinterpret_cast<void(*)()>(exec_mem))(); // function call

    munmap(exec_mem, getpagesize());
}

Which works fine, but as soon as I try to do something even trivial I get a segfault.

I tried to do a simple variable assignment like this:

int x;
auto func() -> void{
    x = 5;
}

and now my function call segfaults. I have changed the buffer size appropriately and am certain the correct memory is being written into the buffer.

What important piece of information am I missing here? Why can't I do this?

P.S. Please don't lecture me on unsafe code, this is a simple personal learning exercise.

1

There are 1 answers

5
tux3 On BEST ANSWER

Ignoring the fact that this is blatant undefined behavior, if you do an assignment of a global variable, the generated code is likely to use relative addressing to reference the variable on some architectures.

That is, the function expects itself and x to be at a given address, and if you move it, things break.

This is what my GCC generates for your test function:

x:
        .zero   4
        .text
        .globl  _Z4funcv
        .type   _Z4funcv, @function
_Z4funcv:
.LFB2:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $5, x(%rip)
        nop
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

Note the movl $5, x(%rip), which means that the code uses its own address (stored in %rip) to compute the position of x and store 5 in it.

So in short, there's no simple way to do what you're trying to do, unless you make sure that your function only has position-independent code. And even then, it's only asking for trouble.