Dereferencing function pointers in C to access CODE memory
Asked Answered
A

2

7

We are dealing with C here. I'm just had this idea, wondering if it is possible to access the point in memory where a function is stored, say foo and copying the contents of the function to another point in memory. Specifically, I'm trying to get the following to work:

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

void foo(){
    printf("Hello World");
}

int main(){

    void (*bar)(void) = malloc(sizeof foo);
    memcpy(&bar, &foo, sizeof foo);


    bar();
    return 0;
}

But running it gives a bus error: Bus error: 10. I'm trying to copy over the contents of function foo into a space of memory bar and then executing the newly created function bar.

This is for no other reason than to see if such a thing is possible, to reveal the intricacies of the C language. I'm not thinking about what practical uses this has.

I'm looking for guidance getting this to work, or otherwise to be told, with a reason, why this won't work

EDIT Looking at some of the answers and learning about read, write, and executable memory, it just dawned upon me that it would be possible to create functions on the fly in C by writing to executable memory.

Anthropomorphic answered 27/11, 2015 at 20:11 Comment(16)
I did not understand the very first statement in main.....Turnbow
Have you checked that e.g. sizeof *foo seems even remotely valid?Subscript
@iharob Stop right here. You are not going to destroy OP's curiosity. Practical applications are unimportant for being curious about something.Find
@iharob Why is this a waste of time? I'm trying to reveal the intricacies of the C language.Anthropomorphic
It is not a feature of the C language, but of the CPU/OS architecture. There were so-called "self-modifying" programs in the days of DOS, which didn't have much memory protection stuff. Today it is much less likely to find an architecture allowing this.Hofmannsthal
" I'm trying to reveal the intricacies of the C language" All you reveal is undefined behaviour. Converting a function pointer to an object pointer is UB.Sinfonietta
Are you sure sizeof foo isn't just going to return the size of the pointer (i.e. 4 or 8)?Baseborn
Didn't realize this is UB behavior.Anthropomorphic
@Olaf False. Converting a function pointer to an object pointer is implementation defined and the special case of converting a function pointer to void* is perfectly well-defined.Find
@FUZxxl mentioned that problem in his answer. Is there a way to get the size of the function and not just the size of the pointer to it?Anthropomorphic
I don't think C has (or even theoretically can have) a facility to do that.Hofmannsthal
@FUZxxl: Please point me at the corresponding section in the standard.Sinfonietta
@Olaf Please tell me where it says that it's undefined behaviour. Additionally, notice that the POSIX API (specifically, dladdr), requires such casts to work. It's unlikely that the C committee intended to make such a basic function undefined behaviour.Find
@FUZxxl: One property of "undefined behaviour" is that if it is not defined by the standard, it cannot be found in the standard. However, I found something myself. It is a common extension (see J.5.7 for function pointer casts), which makes a program non-compliant. So, according to the standard, it is UB. No wonder, because on true harvard architectures code-pointers reside in a completely different space than data-pointers and there is possible no direct data-access to code-space.Sinfonietta
Apart from the whole UB thing, memcpy(&bar, &foo is copying foo to the pointer bar, not to the memory block pointed to by bar.Requiescat
@Anthropomorphic Kind of a cop out answer, but you might find something fitting what you need more by looking at JIT solutions out there unless this is more of a purely academic thing. There you can generate code on the fly, capture the compiled result to a function pointer and execute the resulting machine code through the function pointer.Desiderate
F
8

With standard C, what you try to do is implementation defined behaviour and won't work portably. On a given platform, you might be able to make this work.

The memory malloc gives you is typically not executable. Jumping there causes a bus error (SIGBUS). Assuming you are on a POSIX-like system, either allocate the memory for the function with mmap and flags that cause the memory region to be executable or use mprotect to mark the region as executable.

You also need to be more careful with the amount of memory you provide, you cannot simply take the size of a function and expect that to be the length of the function, sizeof is not designed to provide this kind of functionality. You need to find out the function length using some other approach.

Find answered 27/11, 2015 at 20:18 Comment(10)
How do I determine the size of the function then?Anthropomorphic
@Anthropomorphic That's not possible with standard C and I honestly don't know how you would do that. After compilation, you can use nm -S to find the size of functions. For a start, you could try to hard-code the function size.Find
Converting a function pointer to an object pointer (which includes void *) is not implementation defined, but undefined behaviour. In C, a function is not an object.Sinfonietta
@Olaf: This is actually one of those holes in the spec -- it is never explicitly specified whether a function is an object or value or not.Pyrometallurgy
@Olaf It is not said if functions are situated in data storage. On von Neumann systems, they are.Find
@FUZxxl: Please read the second half of the sentence: "..., the contents of which can represent values". This has nothing to do with CPU spaces. (but the seperation is required to allow support such in an implementation). Note that with memory protection units, you can implement much the same, just at a finer grain than with the fixed harvard approach. This is also the reason sizeof(function) yields the size of the pointer, not that of the function - a function is no object.Sinfonietta
@Olaf “can” is not “must.” It is allowed that an object represents values but not required. For example, a trap representation doesn't represent a value either. sizeof on a function yields the size of a function pointer because it's defined this way, not because functions cannot be objects.Find
@FUZxxl: To cut this nonsense discussion down: Please ask a new question if you still insist of a function being an object in C.Sinfonietta
@FUZxxl: Just found in the standard: "... If the operand points to a function, the result is a function designator; if it points to an object, .... Why the differentiation if a function was an object? Such statements can be found all over the standard. E.g. in the paragraph right before (last sentence).Sinfonietta
@Olaf the standard similarly distinguishes the different basic integer types, even though they might refer to the same underlying type.Find
M
6

On modern desktops, the virtual memory manager is going to get in your way. Memory regions have three types of access: read, write, and execute. On systems where code segments have only execute permission, the memcpy will fail with a bus error. In the more typical case, where only code segments have the execute permission, you can copy the function, but not run, because the memory region that contains bar will not have execute permission.

Also, determining the size of the function is problematic. Consider the following program

void foo( int *x )
{
    printf( "x:(%zu %zu) ", sizeof x, sizeof *x );
}

int main( void )
{
    int x = 0;
    foo( &x );
    printf( "foo:(%zu %zu)\n", sizeof foo, sizeof *foo );
}

On my system, the output is x:(8 4) foo:(1 1) indicating that taking the sizeof a function pointer, or the function itself, is not a supported operation.

Maskanonge answered 27/11, 2015 at 20:19 Comment(2)
Can I somehow give it execute permission?Anthropomorphic
@Anthropomorphic FUZxxl has answered that part of the question. Interacting with the virtual memory manager is not standardized.Maskanonge

© 2022 - 2024 — McMap. All rights reserved.