2004-11-22 10:00:38

by Gerd Knorr

[permalink] [raw]
Subject: var args in kernel?

On Fri, Nov 19, 2004 at 02:41:46PM -0700, Kevin P. Fleming wrote:
> Gerd Knorr wrote:
> >Yet another kobject bug. It uses the varargs list twice in a illegal
> >way. That doesn't harm on i386 by pure luck, but blows things up on
> >amd64 machines. The patch below fixes it.
>
> Is this safe? The normal glibc varargs implementation says you can't
> even call va_start on the same args list twice, you have to use va_copy
> to make a clone and then call va_start on that, _before_ you ever call
> va_start the first time.

Hmm, maybe. I'm not sure who actually implements the varargs (gcc?
Or glibc/kernel?) and whenever the above applies to the kernel as well
or not ...

Cc'ing the kernel list for comments.

Gerd

--
#define printk(args...) fprintf(stderr, ## args)


2004-11-22 10:17:14

by Jakub Jelinek

[permalink] [raw]
Subject: Re: var args in kernel?

On Mon, Nov 22, 2004 at 10:43:12AM +0100, Gerd Knorr wrote:
> On Fri, Nov 19, 2004 at 02:41:46PM -0700, Kevin P. Fleming wrote:
> > Gerd Knorr wrote:
> > >Yet another kobject bug. It uses the varargs list twice in a illegal
> > >way. That doesn't harm on i386 by pure luck, but blows things up on
> > >amd64 machines. The patch below fixes it.
> >
> > Is this safe? The normal glibc varargs implementation says you can't
> > even call va_start on the same args list twice, you have to use va_copy
> > to make a clone and then call va_start on that, _before_ you ever call
> > va_start the first time.
>
> Hmm, maybe. I'm not sure who actually implements the varargs (gcc?
> Or glibc/kernel?) and whenever the above applies to the kernel as well
> or not ...

varargs is implemented entirely by GCC (GCC owns the stdarg.h header
and implements the builtin functions that are used in the va_* macros).

You can call va_start on the same args list twice, both:
void foo (int x, ...)
{
va_list ap, ap2;
va_start (ap, x);
va_start (ap2, x);
...
va_end (ap);
va_end (ap2);
}
and
void foo (int x, ...)
{
va_list ap;
va_start (ap, x);
...
va_end (ap);
va_start (ap, x);
...
va_end (ap);
}
are ok. What you can't do is e.g.
va_list ap;
va_start (ap, x);
bar (x, ap);
bar (x, ap);
va_end (ap);
(i.e. once you pass the ap to some other function, the only thing that you
have to do with it in the caller is va_end).

Jakub

2004-11-22 10:43:31

by Gerd Knorr

[permalink] [raw]
Subject: Re: var args in kernel?

> You can call va_start on the same args list twice, both:
> [ ... ]
> and
> void foo (int x, ...)
> {
> va_list ap;
> va_start (ap, x);
> ...
> va_end (ap);
> va_start (ap, x);
> ...
> va_end (ap);
> }
> are ok.

Fine, then my patch is ok, it does exactly this ;)

> What you can't do is e.g.
> va_list ap;
> va_start (ap, x);
> bar (x, ap);
> bar (x, ap);
> va_end (ap);

This is how the code looked before I fixed it.

Thanks,

Gerd

--
#define printk(args...) fprintf(stderr, ## args)

2004-11-22 11:06:17

by Jan Engelhardt

[permalink] [raw]
Subject: Re: var args in kernel?

>> What you can't do is e.g.
>> va_list ap;
>> va_start (ap, x);
>> bar (x, ap);
>> bar (x, ap);
>> va_end (ap);

In theory, you can't. But the way how GCC (and probably other compilers)
implement it, you can. Because "ap" is just a pointer (which fits into a
register, if I may add). As such, you can copy it, pass it multiple times, use
it multiple times, and whatever you like.

At least that's IIRC the way TCPP recommends you in their helpfile
(va_list src; ... va_list dest = src)



Jan Engelhardt
--
Gesellschaft für Wissenschaftliche Datenverarbeitung
Am Fassberg, 37077 Göttingen, http://www.gwdg.de

2004-11-22 11:35:38

by Jakub Jelinek

[permalink] [raw]
Subject: Re: var args in kernel?

On Mon, Nov 22, 2004 at 12:03:56PM +0100, Jan Engelhardt wrote:
> >> What you can't do is e.g.
> >> va_list ap;
> >> va_start (ap, x);
> >> bar (x, ap);
> >> bar (x, ap);
> >> va_end (ap);
>
> In theory, you can't. But the way how GCC (and probably other compilers)
> implement it, you can. Because "ap" is just a pointer (which fits into a
> register, if I may add). As such, you can copy it, pass it multiple times, use
> it multiple times, and whatever you like.

That's exactly the wrong assumption.
On some Linux architectures you can, on others you can't.
Architectures where va_list is a char or void pointer include e.g.:
i386, sparc*, ppc64, ia64
Architectures where va_list is something different, usually struct { ... } va_list[1];
or something similar:
x86_64, ppc32, alpha, s390, s390x

In the latter case, you obviously can't do va_list dest = src and
if you do bar (x, ap); the content of the struct pointed by ap is changed
after the call, therefore you can't use it for other routines
(as it depends on where exactly the called function stopped with va_arg).

Jakub

2004-11-22 11:41:10

by Jan Engelhardt

[permalink] [raw]
Subject: Re: var args in kernel?

>> In theory, you can't. But the way how GCC (and probably other compilers)
>> implement it, you can. Because "ap" is just a pointer (which fits into a
>> register, if I may add). As such, you can copy it, pass it multiple times, use
>> it multiple times, and whatever you like.
>
>That's exactly the wrong assumption.
>On some Linux architectures you can, on others you can't.
>Architectures where va_list is a char or void pointer include e.g.:
>i386, sparc*, ppc64, ia64
>Architectures where va_list is something different, usually struct { ... } va_list[1];
>or something similar:
>x86_64, ppc32, alpha, s390, s390x

Yeah I originally had that in mind but did not write it out ;)
What would the struct look like? Because if the following was true, then you
could also use a pointer:

int my_printf(const char *fmt, struct { some ints, some char's, whatever } arg)
{
struct giveItAName *ap = &arg;
callanotherfunc(fmt, ap);
callanotherfunc(fmt, ap);
}

>In the latter case, you obviously can't do va_list dest = src and

Mmh, I could, because GCC is wise enough to copy whole structs. This is BTW
done a LOT in the kernel sources. Just compile it with -Waggreagte-return and
ye'll get a whole whopping lot of warnings from GCC. For
linux-2.[46].\d+.tar.bz2.

>if you do bar (x, ap); the content of the struct pointed by ap is changed
>after the call, therefore you can't use it for other routines

Who said the struct is changed? The compiler could use some magic to copy
above-metioned "arg" to a temporary which is only valid during my_printf() and
callees.
Who says the struct has to be changed? And even if it was about to, you could
copy it.

>(as it depends on where exactly the called function stopped with va_arg).

Don't get me wrong, I'm just asking... in a pessimistic fashion.



Jan Engelhardt
--
Gesellschaft für Wissenschaftliche Datenverarbeitung
Am Fassberg, 37077 Göttingen, http://www.gwdg.de

2004-11-22 12:41:13

by Andreas Schwab

[permalink] [raw]
Subject: Re: var args in kernel?

Jan Engelhardt <[email protected]> writes:

>>> In theory, you can't. But the way how GCC (and probably other compilers)
>>> implement it, you can. Because "ap" is just a pointer (which fits into a
>>> register, if I may add). As such, you can copy it, pass it multiple times, use
>>> it multiple times, and whatever you like.
>>
>>That's exactly the wrong assumption.
>>On some Linux architectures you can, on others you can't.
>>Architectures where va_list is a char or void pointer include e.g.:
>>i386, sparc*, ppc64, ia64
>>Architectures where va_list is something different, usually struct { ... } va_list[1];
>>or something similar:
>>x86_64, ppc32, alpha, s390, s390x
>
> Yeah I originally had that in mind but did not write it out ;)
> What would the struct look like? Because if the following was true, then you
> could also use a pointer:

You left out an important detail:

> int my_printf(const char *fmt, struct { some ints, some char's, whatever } arg)

int my_printf(const char *fmt, struct { some ints, some char's, whatever } arg[1])

which the same as

int my_printf(const char *fmt, struct { some ints, some char's, whatever } *arg)

Andreas.

--
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstra?e 5, 90409 N?rnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."

2004-11-22 22:58:22

by Bill Davidsen

[permalink] [raw]
Subject: Re: var args in kernel?

Jakub Jelinek wrote:
> On Mon, Nov 22, 2004 at 12:03:56PM +0100, Jan Engelhardt wrote:
>
>>>> What you can't do is e.g.
>>>> va_list ap;
>>>> va_start (ap, x);
>>>> bar (x, ap);
>>>> bar (x, ap);
>>>> va_end (ap);
>>
>>In theory, you can't. But the way how GCC (and probably other compilers)
>>implement it, you can. Because "ap" is just a pointer (which fits into a
>>register, if I may add). As such, you can copy it, pass it multiple times, use
>>it multiple times, and whatever you like.
>
>
> That's exactly the wrong assumption.
> On some Linux architectures you can, on others you can't.
> Architectures where va_list is a char or void pointer include e.g.:
> i386, sparc*, ppc64, ia64
> Architectures where va_list is something different, usually struct { ... } va_list[1];
> or something similar:
> x86_64, ppc32, alpha, s390, s390x
>
> In the latter case, you obviously can't do va_list dest = src and
> if you do bar (x, ap); the content of the struct pointed by ap is changed
> after the call, therefore you can't use it for other routines
> (as it depends on where exactly the called function stopped with va_arg).

Why can't you do dest=src? Assignment of struct to struct has been a
part of C since earliest times. I used it in ~1990 in code which ran on
Z80, Multics, M68k, VAX and Cray2, and it worked without any ifdefs (for
that, there were "just a few" for other issues like 8 vs. 9 bit char, etc).

--
-bill davidsen ([email protected])
"The secret to procrastination is to put things off until the
last possible moment - but no longer" -me

2004-11-22 23:22:52

by Jakub Jelinek

[permalink] [raw]
Subject: Re: var args in kernel?

On Mon, Nov 22, 2004 at 04:42:43PM -0500, Bill Davidsen wrote:
> Assignment of struct to struct has been a
> part of C since earliest times. I used it in ~1990 in code which ran on
> Z80, Multics, M68k, VAX and Cray2, and it worked without any ifdefs (for
> that, there were "just a few" for other issues like 8 vs. 9 bit char, etc).

It is not a struct on those arches, but array of structs.
Just try:
#include <stdarg.h>

void foo (int x, ...)
{
va_list ap, ap2;
va_start (ap, x);
ap2 = ap;
va_end (ap);
}
on say x86_64 or ppc32 and you'll see what I mean:
test.c:7: error: incompatible types in assignment

That's why the standard has va_copy so that you can do the
copying portably.

Jakub

2004-11-22 23:57:25

by Andreas Schwab

[permalink] [raw]
Subject: Re: var args in kernel?

Bill Davidsen <[email protected]> writes:

> Why can't you do dest=src? Assignment of struct to struct has been a part
> of C since earliest times.

It's not a struct, it's an array (of one element of struct type). You
can't assign arrays.

Andreas.

--
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstra?e 5, 90409 N?rnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."

2004-11-23 14:08:28

by Jan Engelhardt

[permalink] [raw]
Subject: Re: var args in kernel?

>> Why can't you do dest=src? Assignment of struct to struct has been a part
>> of C since earliest times.
>
>It's not a struct, it's an array (of one element of struct type). You
>can't assign arrays.

int callme(const char *fmt, struct { ... } argp[1]) {
dest = *argp;
}

Maybe that way?


Jan Engelhardt
--
Gesellschaft für Wissenschaftliche Datenverarbeitung
Am Fassberg, 37077 Göttingen, http://www.gwdg.de

2004-11-23 15:11:19

by Andreas Schwab

[permalink] [raw]
Subject: Re: var args in kernel?

Jan Engelhardt <[email protected]> writes:

>>> Why can't you do dest=src? Assignment of struct to struct has been a part
>>> of C since earliest times.
>>
>>It's not a struct, it's an array (of one element of struct type). You
>>can't assign arrays.
>
> int callme(const char *fmt, struct { ... } argp[1]) {
struct { ... } dest[1];
> dest = *argp;
> }
>
> Maybe that way?

Maybe you should just try.

Andreas.

--
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstra?e 5, 90409 N?rnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."

2004-11-23 17:40:21

by Andreas Schwab

[permalink] [raw]
Subject: Re: var args in kernel?

Jan Engelhardt <[email protected]> writes:

>>>>It's not a struct, it's an array (of one element of struct type). You
>>>>can't assign arrays.
>>>
>>> int callme(const char *fmt, struct { ... } argp[1]) {
>> struct { ... } dest[1];
>>> dest = *argp;
>>> }
>>>
>>> Maybe that way?
>>
>>Maybe you should just try.
>
> I did not say that 'dest' was an array too

But we are talking about va_list, which _is_ an array on some
architectures, and which this thread is all about.

Andreas.

--
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstra?e 5, 90409 N?rnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."

2004-11-24 00:09:41

by Jan Engelhardt

[permalink] [raw]
Subject: Re: var args in kernel?

>>>It's not a struct, it's an array (of one element of struct type). You
>>>can't assign arrays.
>>
>> int callme(const char *fmt, struct { ... } argp[1]) {
> struct { ... } dest[1];
>> dest = *argp;
>> }
>>
>> Maybe that way?
>
>Maybe you should just try.

I did not say that 'dest' was an array too, and in fact, was not thinking of
such, but more like:

int foo(struct bar argp[1]) {
struct bar dest;
dest = *argp;
}