varargs

Rex Ballard rb at ccird2.UUCP
Fri Apr 25 08:11:22 AEST 1986


In article <5302 at alice.uUCp> ark at alice.UucP (Andrew Koenig) writes:
>> I have always heard that varargs is not very portable.  Is this really
>> true?  If you know of any machine that is running 4.X BSD, System III/V,
>> Xenix or any other real unix that doesn't support varargs, please let
>> me know.
>
>If you find any machine with a sensible architecture that cannot
>be made to support varargs, I would greatly like to hear about it.
>
The concern about about varargs is brought up in "Software Tools" by
Kernigan and Plauger.  The main concern is that certain systems and
languages have the caller push the arguments, and have the callee destroy
them when they are finished.  This is common practice in Fortran and
Pascal.  The big concern with C is that there might be a compiler out
there which has C/Pascal|Fortran compatibility.  Since these compilers
seem to be the exception rather than the rule, and newer versions of
these languages are tending to follow C conventions, this is not likely
to be a problem for *most* environments.

I have heard that there are a few "peculiar"
compilers available for the Atari ST, Mac, and Amiga, which, although
not "kosher", CAN support most varargs situations.  The problems that
can occur are "intelligent" compilers which put arguments directly
into registers rather than on the stack.  Appearantly, this can be
a real problem if you wish to "intercept" a system call at the
application side.  Why do so many of the micro operating systems
insist on putting arguments in registers!!!.  Even MS-DOS expects
arguments in registers rather than on the stack.

The C frame is a more sensible archetecture, but not the only one.
Since you specifically asked about UNIX based systems, you can be
relatively unconcerned.

There are non-portable ways to USE varargs.  The classic is to put
a long indicator into the "format argument" and pass an int. Do this
with a couple of arguments where ints are 16 bits, and longs are 32 bits,
and you will return to "never never land".  The normal usages
(printf, scanf,...) aren't usually a problem, but there are a few
"mix and match" functions using varargs which selectively mix and match
various modifiable stack addresses, and will "core dump" the whole
system.

try this: (comments are for 16 bit ints, 32 bit longs, 32 bit pointers).

main()
{
    /* top of frame = return address */
    int i;
    long l;
    scanf("%d %d",&i);	/* second argument missing, clobbers frame */
    scanf("%d",i);	/* cute "poke" of i, can demolish code or core dump */
    scanf("%ld",&i);	/* extra 16 bits is usually frame pointer or return
			   address */
    scanf("%d",l);	/* 16 bits of "garbage" plus your number possibly
			   munged beyond recognition due to byte ordering */
}

Lint will pass it, assuming printf is declared /*VARARGS*/, and the
compiler won't even sneeze.  But running it will give you "buss error
core dumped" or something equally nasty.  This is one of the few areas
where lint will not protect you.

Anyone figured out a syntax checker for these functions?
Of course, you should use a syntax checker for any of these functions.

Another cute trick I've seen is using varargs to pass integers to
functions which assign to pointers. Such as:

main()
{
    int read();
    init(0x20L,&read);	/* set exception vector */
}

/*VARARGS*/
init(a,b)
char **a,*b,**i;
{
	*a=b;	/* poke absolute value with a valid address */
}

This is the ultimate in unportability, but is frequently "hidden" in
"hostile" PC programs, typically to mount new device drivers and
remove them when the code is complete.  Just try plugging one of these
into unix unmodified :-).



More information about the Comp.unix.wizards mailing list