问题描述
我了解到,__builtin_object_size
应该提供有关对象大小的信息,如果可以在编译时确定它,或者对malloc
的相关调用使用属性alloc_size
。我想我用ubuntu 20.04上的gcc 9.3使用
gcc -g -std=gnu11 -Wall -O2 compstrcpyos.c -o compstrcpyos.exe -lbsd
该代码至少应找到doit
的大小,但不是。结果是-1。
void doit(char *buf1,char *buf2,const char *src) {
printf("bos1=%zd,bos2=%zd\n",__builtin_object_size(buf1,0),__builtin_object_size(buf2,0));
strlcpy(buf1,src,0));
strlcpy(buf2,0));
for (size_t i=0; i < strlen(buf1); ++i) {
buf1[i] = tolower(src[i]);
}
for (size_t i=0; i < strlen(buf2); ++i) {
buf2[i] = tolower(src[i]);
}
}
void *malloc(size_t s) __attribute__((alloc_size (1)));
int main(int argc,char *argv[]) {
if (argc < 2) { return -1; }
char buf1[20];
char *buf2 = malloc(20);
printf("bos1=%zd,0));
doit(buf1,buf2,argv[1]);
printf("%s\n%s\n",buf1,buf2);
}
运行代码即可。
$ ./compstrcpyos.exe ABCDefghijklmnopq
bos1=20,bos2=20
bos1=-1,bos2=-1
abcdefghijklmnopq
abcdefghijklmnopq
我尝试添加选项-fsanitize=object-size
,在调用__builtin_object_size
时尝试使用常量1、2和3而不是0,但是在doit
中却没有得到20。我还需要在第二行(即doit
中)获得20、20。
任何帮助表示赞赏。
解决方法
通过函数调用间接删除编译器对确定大小充满信心的能力。从理论上讲,该函数可以由其他调用者调用,包括翻译单元(文件)之外的调用者。如果您在static
函数声明中添加一个doit
限定符,表示没有外部调用者,则有可能(在此特定示例中)进行确定的编译时确定。>
就其价值而言,__builtin_object_size
并不是说“这东西有多大”的惯用方式。它似乎打算like the example在一种作用域内的超级防御性编码中使用。
将具有显式大小的缓冲区传递给函数的惯用方式是传递缓冲区以及缓冲区的大小作为单独的参数(或作为上下文结构或其他内容的一部分)
,我认为您高估了编译器的作用范围:
我知道,
__builtin_object_size
应该提供有关对象大小的信息,如果可以在编译时确定的话
到目前为止,很好。
或对malloc的相关调用使用属性
alloc_size
。
但这不是析取符(“或”)。编译器只能在编译时知道什么。因此,除了在编译时确定大小之外,别无选择。现在,如果动态分配对象,则编译器仍可以确定其大小,条件是:
- 它是由编译器认为是动态分配函数的函数分配的,并且
- 编译器可以从参数推断出动态分配函数所分配对象的大小,并且
- 编译器可以确定这些参数的值。
对于前两点,编译器依赖于alloc_size
属性。如果分配函数未使用此属性标记,则编译器无法确定对象的大小。因此,这是一个连词(“和”)。但这还不够;编译器还需要能够确定分配对象时使用的参数的值。
要通过函数调用进行工作,必须基本上内联函数调用。如果只在一个地方调用静态函数,则通常将其内联。 (它们可能内嵌在其他位置,但是如果从多个调用站点调用(或可能调用)大型函数,则不太可能内联。)
简而言之,如果您说:
__builtin_object_size
应该提供有关对象大小的信息(如果可以在编译时确定的话), 这需要 对{{1 }}具有属性malloc
,并且可以在编译时确定其参数。