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

Posted

技术标签:

【中文标题】使用数组作为可变参数函数的最后一个命名参数会导致缓冲区欠载吗?【英文标题】:Can using an array as the last named argument to a variadic function result in a buffer underrun? 【发布时间】:2018-07-22 21:32:30 【问题描述】:

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

因为这个参数的地址用在va_start()宏中,所以不能声明为寄存器变量,也不能声明为函数或数组类型。

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

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

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

另外,printf 系列函数呢?那些使用字符数组作为他们最后命名的参数。他们如何不遇到问题?

【问题讨论】:

我认为“函数”在这里的意思是“函数名”,而不是“函数调用的结果”。 【参考方案1】:

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

函数不能有数组类型的参数函数类型,因为任何作为函数参数的数组声明都会被转换为指针,函数的参数也是如此输入。

C standard 的第 6.7.6.3 节详细说明了函数声明器,其中陈述如下:

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

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

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

【讨论】:

【参考方案2】:

您正在想象按值调用,这不会发生。

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

我怀疑在某些编译器中, 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 在查找下一个参数时跳过错误的字节数。

【讨论】:

以上是关于使用数组作为可变参数函数的最后一个命名参数会导致缓冲区欠载吗?的主要内容,如果未能解决你的问题,请参考以下文章

Scala可变参数列表,命名参数和参数缺省

JAVA基础_可变参数

如何循环遍历动态大小的数组并将属性作为参数传递给可变参数函数?

java中参数变量具体是啥,可以干啥,有啥作用,

Python函数中的必选/默认/可变/关键字/命名参数

Java重要技术语法之可变参数