views:

119

answers:

3

I'd like to copy and call a function, but the code below segfaults when calling the buffer. What do I have to change? (Linux, x86)

#include <string.h>
#include <malloc.h>
#include <stdio.h>

int foo () { return 12; }
void foo_end () {}

int main () {
  int s = (unsigned long long) foo_end - (unsigned long long) foo;
  int (*f) () = (int (*)()) malloc (s);
  memcpy ((void*) f, (const void*) foo, s);
  printf ("%d %d\n", f (), foo ());
}

EDIT: Working solution:

#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

int foo () { return 12; }
void foo_end () {}

int main () {
  int s = (unsigned long long) foo_end - (unsigned long long) foo;
  int (*f) () = (int (*)()) malloc (s);
  memcpy ((void*) f, (const void*) foo, s);
  long ps = sysconf (_SC_PAGESIZE);
  void *fp = (void*) ((unsigned long long) f & ~((unsigned long long) (ps-1)));
  if (mprotect ((void*) fp, ps, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
  printf ("%d %d\n", f (), foo ());
}
+8  A: 

Whoa, that code has so many issues.

  1. You can't know that the functions are laid out sequentially in memory, with no padding between them
  2. You can't know that the pointers to two functions are subtractable
  3. You can't know that memory returned by malloc() can be called into

In short, don't do this.

Update:

In Linux, I think you can use mprotect() to set the permissions on a block of memory. I thought this needed root, but apparently not (as long as you're in your own process' memory).

unwind
I agree, do not do this :)
MarkR
It is just a first test, currently, but later I'll need to fill a buffer with my own assembly instructions. g++ sets `s` to 16, so the layout of the functions is okay. How can I set the permissions to call into memory allocated by malloc?
Thomas
They're not knowable *in general*, but with some additional information, you can know all three. The first is knowable if you're in some control over the linker. The second is knowable if you know the compiler. The third is knowable if you know the OS and its settings. Also, I see no reason to think root would be the only account that can set execute permission on a process's own memory.
Rob Kennedy
No root priviledges are needed on my system (and I need this code only locally, so portability is not important).
Thomas
@Thomas: If you want to allocate an executable area of memory, the more portable way to do it is directly with `mmap()`, rather than `malloc()` and `mprotect()`.
caf
I'll try `mmap (0, s, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_32BIT | MAP_ANONYMOUS | MAP_NORESERVE | MAP_UNINITIALIZED, -1, 0);` - I hope that I got the flags right for later r/w access to the block to be as efficient as with malloc.
Thomas
+2  A: 

Potentially you're using an OS which does not grant execute permission to data segments.

Some environments will protect data pages against execution, to avoid various types of security problems (or exploits for them).

Consider calling mprotect() to enable execute for that page and report what happens.

MarkR
Works now, thank you!
Thomas
A: 

This is a common issue among embedded systems folk. This technique is often times used for copying from Read-Only Memory into Random-Access Memory (write and read capable). There is no elegant nor standard solution using standard C or C++.

A simpler solution is to use the Linker to define some new, non-standard, segments. Use non-standard #pragma to instruct the compiler to place the function into a new segment. Use non-standard compiler directive to access the beginning address and ending address of this segment. This will allow you to get the size of the function.

A safer method for the destination is to create another segment with executable and write attributes. Copy the data in the function segment into this executable segment. Set up a function pointer to point to the start of this segment. Execute the function via the pointer.

Another solution is to perform this in assembly language. Often, assemblers give you more freedom (to shoot your foot) to manipulate memory like this in a lower level.

Also, review your operating system loader, memory attributes and protection schemes. Some OSes may restrict this kind of behavior to Kernel privilege or higher.

Thomas Matthews