Reputation: 121
Suppose we have some C code that calls upon a function though a function pointer, whether it be through a function pointer table or a function pointer passed as parameter or other, like so:
/* ... some other code .. */
void (*f)(void) = something; // f function pointer to some function
(*f)();
This should be compiled to (or something equivalent)
mov %rcx, [something] ; here f=ecx
callq *%rcx
Question: does %ecx
always point to a function prologue or can it point to a small peice of code at the end on a function?
Example:
void big_func(){
/* lots of code here */
printf("bar");
printf("foo");
}
void small_func(){
printf("foo");
With big_func
C compiled to
; some more code up here
1: 48 8d 3d c4 0e 00 00 lea %rdi,[0xec4+%rip] ;ptr to "bar"
2: b8 00 00 00 00 mov %eax,$0x0
3: e8 e6 fe ff ff callq 1030 <printf@plt>
4: 48 8d 3d b7 0e 00 00 lea %rdi,[0xeb7+%rip] ;ptr to "foo"
5: b8 00 00 00 00 mov %eax,$0x0
6: e8 d5 fe ff ff callq 1030 <printf@plt>
7: b8 00 00 00 00 mov %eax,$0x0
8: 5d pop %rbp
9: c3 ret
Is it possible for a call to small_func
to point to 4:
as it's entry point? Does this happen ever (with a generic compiler like gcc) or only with some human modifying the assembly code behind the scenes?
Question limits:
Additional mini-question: What happens would happen of one intentionally modifies the function pointer to skip some bytes from a function prologue? Is this considered undefined behaviour?
Example:
void (*f)(void) = something; // f function pointer to some function
f=(void (*func_ptr)(void)) ((*char)f+2)
(*f)(); //skips `push ebp`
EDIT: I should have been more clear as of why this question is asked. It is in the context of a research master, seeking a new way to mitigate ROP based attacks at a very low software or hardware level. If it were possible for indirect calls to point somewhere else than a function prologue it could break one of our tag-based implementation (missing the tag and terminating the program after incorrectly detecting an attack)
Upvotes: 1
Views: 491
Reputation: 26796
In C, like most languages, functions have a very specific meaning, purpose and implementation. In invocation, a function pointer has to work just like a function. We cannot invoke code that isn't a function, whether direct call or function pointer. The function generally doesn't know whether it was invoked directly or by pointer — it must accept the arguments passed, perform its function and return to the caller (usually), no matter how it was called (directly or by function pointer). In C, functions have a single entry point.
There is no concept in C of a snippet of code that isn't a function, that can be transferred to (or invoked) by function pointer. (C has labels and goto's, but there are no label pointers, or label variables or label variable goto's, for example.)
Not all functions require prologue, they only have to be able to accept their arguments (do something) and return to the caller — that doesn't necessarily require prologue or epilogue, but they are still functions (with a single entry point).
Upvotes: 1
Reputation: 68099
The function pointer will always reference the beginning of the function. C Standard does not allow casting from other pointer types. It invokes an Undefined Behaviour.
But the particular implementation may generate the correct code especially if the function does not set the stack frame. But in most cases it will fail
But it makes no sense at all - you simple should split the @big@ function into two smaller ones and call them when needed without pseudo tricks.
Upvotes: 2