4

I found this paragraph in the man page for stdarg.h:

Because the address of this parameter is used in the va_start() macro, it should not be declared as a register variable, or as a function or an array type.

So, register variable I understand, since a register can't be addressed with a pointer. Function I understand, since you would get the return value, which would use immediate addressing rather than address register indirect addressing.

I'm curious about what would happen if you used an array as the parameter. Say you use an array of three int types. Would this result in the first element of the array being used as the last named parameter, while the next two elements would end up being used as the values for the variable arguments? This would be a buffer underrun.

I'm also wondering if this would result in a security vulnerability, e.g. someone could input elements of the array and have the function do something it wasn't supposed to do because it thinks the extra array elements are variable parameters.

Also, what about the printf family of functions? Those use character arrays as their last named arguments. How do they not run into problems?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Shoblade X
  • 363
  • 2
  • 9

2 Answers2

0

This looks like an error in the man page.

A function cannot have a parameter of array type or function type, since any array declaration as a function parameter is converted to a pointer, as is a parameter of function type.

Section 6.7.6.3 of the C standard detailing Function Declarators states the following:

7 A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

8 A declaration of a parameter as "function returning type" shall be adjusted to "pointer to function returning type", as in 6.3.2.1

So what this means as far as va_start is that the last named parameter can't use the register specifier. Arrays and functions don't matter because they are adjusted to pointers.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

You are imagining call by value, which does not happen.

Arrays and functions are passed as addresses.

I suspect that in some compilers, sizeof() returns (or once returned) the size of an array, and not the size of a pointer to the first element. I'm not sure about functions.

This code:

#include <stdio.h>

void test(void (*f)(), int a[3]) {
    printf("sizeof(f): %lu\n", sizeof(f));
    printf("sizeof(a): %lu\n", sizeof(a));
    printf(" f: %p\n", f);
    printf("&f: %p\n", &f);
    printf(" a: %p\n", a);
    printf("&a: %p\n", &a);
}

void foo() {}

int main() {
    int ints[3] = { 1, 2, 3 };
    test(foo, ints);
}

Gives me this warning when I compile it with gcc:

address.c: In function ‘test’:
address.c:6:38: warning: ‘sizeof’ on array function parameter ‘a’ will return size of ‘int *’ [-Wsizeof-array-argument]
     printf("sizeof(a): %lu\n", sizeof(a));
                                      ^
address.c:4:28: note: declared here
 void test(void (*f)(), int a[3]) {
                            ^

This would seem to imply that the compiler authors suspect programmers may be confused about what this should be, so there may be (or have once been) disagreement among compiler writers too.

If sizeof() were to return the size of the array, and not the size of the pointer passed on the stack, that could cause va_start/va_arg to skip over the wrong number of bytes when looking for the next argument.

Andru Luvisi
  • 24,367
  • 6
  • 53
  • 66