可以使用数组作为可变参数函数的最后一个命名参数导致缓冲区欠载?

时间:2018-02-12 16:29:51

标签: c function variadic-functions variadic

我在stdarg.h的手册页中找到了这一段:

  

因为在va_start()宏中使用了此参数的地址,所以不应将其声明为寄存器变量,也不应声明为函数或数组类型。

因此,我理解寄存器变量,因为寄存器不能用指针寻址。函数我理解,因为你会得到返回值,它将使用立即寻址而不是地址寄存器间接寻址。

我很好奇如果你使用数组作为参数将会发生什么。假设您使用三种int类型的数组。这会导致数组的第一个元素被用作最后一个命名参数,而接下来的两个元素最终会被用作变量参数的值吗?这将是缓冲区不足。

我还想知道这是否会导致安全漏洞,例如:有人可以输入数组的元素并让函数做一些它不应该做的事情,因为它认为额外的数组元素是可变参数。

另外,printf函数系列怎么样?那些使用字符数组作为它们的最后命名参数。他们怎么没遇到问题?

2 个答案:

答案 0 :(得分:0)

这看起来像是手册页中的错误。

函数不能具有数组类型函数类型的参数,因为作为函数参数的任何数组声明都会转换为指针,因为它是函数的参数类型。

C standard详细说明函数声明符的第6.7.6.3节声明如下:

  

7 参数声明为“数组类型”应调整为“限定类型指针”,其中类型限定符(如果有)是   在数组类型派生的[和]内指定的那些。如果   关键字static也出现在数组类型的[和]中   派生,然后对每个函数的调用,值的   相应的实际参数应提供对第一个的访问权限   数组的元素,其元素至少与指定的元素一样多   大小表达。

     

8 作为“函数返回类型”的参数声明应调整为“指向函数返回类型的指针”,如6.3.2.1

那么就va_start而言,这意味着最后一个命名参数不能使用register说明符。数组和函数无关紧要,因为它们被调整为指针。

答案 1 :(得分:0)

你正在想象价值的呼唤,这种情况不会发生。

数组和函数作为地址传递。

我怀疑在某些编译器中,sizeof()返回(或一次返回)数组的大小,而不是指向第一个元素的指针的大小。我不确定功能。

此代码:

#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);
}

当我用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]) {
                            ^

这似乎意味着编译器作者怀疑程序员可能会对此应该是什么感到困惑,因此编译器编写者之间可能存在(或曾经存在过)分歧。

如果sizeof()要返回数组的大小,而不是返回堆栈上传递的指针的大小,那么在查找下一个参数时,这可能会导致va_start / va_arg跳过错误的字节数。 / p>