Re: [PATCH] static_call: fix function type mismatch

From: Peter Zijlstra
Date: Wed Mar 24 2021 - 13:37:24 EST


On Wed, Mar 24, 2021 at 05:45:52PM +0100, Rasmus Villemoes wrote:
> Sorry, I think I misread the code. The static calls are indeed
> initialized with a function with the right prototype. Try adding
> "preempt=full" on the command line so that we exercise these lines
>
> static_call_update(cond_resched,
> (typeof(&__cond_resched)) __static_call_return0);
> static_call_update(might_resched,
> (typeof(&__cond_resched)) __static_call_return0);
>
> I would expect that to blow up, since we end up calling a long (*)(void)
> function using a function pointer of type int (*)(void).

Note that on x86 there won't actually be any calling of function
pointers. See what arch/x86/kernel/static_call.c does :-)

But I think some of this code might need some __va_function() love when
combined with CFI.

But yes, this is why I think something like -fcdecl might be a good
idea, that ought to tell the compiler about the calling convention,
which ought to be enough for the compiler to figure out that this magic
really is ok.

Notable things we rely on:

- caller cleanup of stack; the function caller sets up any stack
arguments and is also responsible for cleanin up the stack once the
function returns.

- the return value is in a register.

Per the first we can call a function that has a partial (empty per
extremum) argument list. Per the second we can call a function with a
different return type as long as they all fit in the same register.

The calling of a 'long (*)()' function for a 'int (*)()' type then
becomes idential to something like: 'int x = (long)y', and that is
something C is perfectly fine with.

We then slightly push things with the other __static_call_return0()
usage in the kernel, where we basically end up with: 'void *x =
(long)y', which is something C really rather would have a cast on.