Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a function pointer to a type that accepts more arguments

As far as I know, casting between incompatible function pointers, eg:

void aUnaryFunction(int a1)
{
    /* .... */
}

void doSomethingWithFn()
{
    typedef void(*BinaryFn)(int, const char*);
    BinaryFn aBinaryFunction = (BinaryFn) &aUnaryFunction;
    aBinaryFunction (3, "!!!");
}

should never be done, as is 'undefined behaviour' according to the C standard.

However I can't see why, given the way function calls work in C, this example isn't safe. All I'm doing is disregarding an argument.

Assuming that the treatment of an int first argument is consistent, all that will happen is that the const char* will be placed in a register when doSomethingWithFn() calls aBinaryFunction, aUnaryFunction will run as expected, and the const char* may be overwritten during aUnaryFunction, but that's fine because nothing else will use it anyway.

Am I missing something here, or is this in fact safe? (Or something in between the two, or both?)

like image 534
Chris Devereux Avatar asked Dec 22 '22 13:12

Chris Devereux


2 Answers

The problem is that there isn't a single "way function calls work in C" - there's only the way that function calls work on a particular C implementation.

As a concrete example, "callee clean-up" calling conventions (like x86 stdcall) require the callee to know how many parameters were pushed on to the stack to perform the correct cleanup, which would be subverted in your example.

like image 117
caf Avatar answered Jan 11 '23 22:01

caf


This boils down to calling conventions. What if arguments are "sent" in reverse order for example? Or if there's a standard procedure that must be done after the function call that relies on the right number of arguments?

What you propose might work, but you shouldn't rely on this and the Standard attributes this as undefined behavior for a reason.

like image 21
sharptooth Avatar answered Jan 11 '23 22:01

sharptooth