is casting a variadic function to a non-variadic one legal?

C

Colin Walters

Hi,

I have a program which had a little function that always returned TRUE,
it looked like this:

int
always_true_function (void *dummy, ...)
{
return 1;
}

Now, at one point I was calling this via a function pointer that was
defined without the varargs, like this:

typedef int (*testfunc) (void *dummy);

int
main (int argc, char **argv)
{
testfunc f = (testfunc) always_true_function;
if (f (NULL))
return 0;
return 1;
}

This all worked fine up until recently, when I heard from one user who was
trying to run the program on AMD64 was getting very mysterious segfaults.

On my machine (i386, gcc 3.3.2), the above test program works fine. On
his machine (AMD64, gcc 3.3.2 + propolice patch), it segfaults.

It turned out I didn't actually need the varargs, but I'm curious - who is
at fault here? Is my code wrong, or is his gcc broken?
 
C

Chris Torek

int
always_true_function (void *dummy, ...)
{
return 1;
}

Now, at one point I was calling this via a function pointer that was
defined without the varargs, like this:

typedef int (*testfunc) (void *dummy);

int
main (int argc, char **argv)
{
testfunc f = (testfunc) always_true_function;
if (f (NULL))
return 0;
return 1;
}

This all worked fine up until recently, when I heard from one user who was
trying to run the program on AMD64 was getting very mysterious segfaults.

On my machine (i386, gcc 3.3.2), the above test program works fine. On
his machine (AMD64, gcc 3.3.2 + propolice patch), it segfaults.

It turned out I didn't actually need the varargs, but I'm curious - who is
at fault here? Is my code wrong, or is his gcc broken?

Your code is technically wrong (or "illegal" or any other number of
words that do not really carry the right meaning). Specifically,
the call:

f(NULL)

invokes undefined behavior by calling a function through a pointer
that has the wrong type. In this case, the AMD64 system probably
uses different call sequences for "varying arguments" vs "fixed
arguments", but in principle this can even happen if you call a
function whose actual type is (e.g.) void(void *) instead of
int(void *).

You can correct the call (without any structural changes to the
code itself, such as changing the name and/or type of
always_true_function()) in at least two ways:

if (((int (*)(void *, ...))f)(NULL)) ...

will convert the function pointer stored in "f" back to the correct
type, then (correctly) call the actual function always_true_function();
or you can insert an intermediate function:

int always_true_function(void *dummy, ...) { return 1; }

int adapter_for_always_true_function(void *passthrough) {
return always_true_function(passthrough);
}
...
testfunc f = adapter_for_always_true_function;

The adapter serves as a sort of "pipe fitting" to connect the
"two-inch pipeline" (varying argument function, in this case)
to the "3/4-inch hose" (non-varying call).

The cast method works, but requires great care -- you must always
match the converted-to type just before the call to the actual
type of the target function. This may well defeat the entire
purpose of using function pointers in the first place, so the
adapter method is more general.

It also acts as a kind of lesson: any time you have a pointer cast
in C code, be suspicious. If there is a pointer-cast-free way to
rewrite the code, that second version probably has fewer bugs. :)
 
B

Ben Pfaff

Colin Walters said:
I have a program which had a little function that always returned TRUE,
it looked like this:

int
always_true_function (void *dummy, ...)
{
return 1;
}

Now, at one point I was calling this via a function pointer that was
defined without the varargs, like this:

typedef int (*testfunc) (void *dummy);

That's undefined behavior. You're not supposed to call a varargs
function through a non-varargs prototype.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,139
Messages
2,570,805
Members
47,355
Latest member
MavoraTech

Latest Threads

Top