__builtin_object_size总是returns-1

__builtin_object_size always returns -1

我明白了,__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, __builtin_object_size(buf1, 0));
  strlcpy(buf2, src, __builtin_object_size(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, bos2=%zd\n",
         __builtin_object_size(buf1, 0),
         __builtin_object_size(buf2, 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。 任何帮助表示赞赏。

通过函数调用间接删除编译器确定大小的信心的能力。理论上,该函数可以被其他调用者调用,包括翻译单元(文件)之外的调用者。如果您向 doit 函数声明添加 static 限定符,表示永远不会有外部调用者,那么(在这个特定示例中)就可以做出明确的 compile-time 确定。

就其价值而言,__builtin_object_size 并不是说​​“这东西有多大”的惯用方式。它似乎打算以 like the example 的方式用于一个范围内的超级防御编码。

将具有明确大小的缓冲区传递给函数的惯用方法是将缓冲区及其大小作为单独的参数(或作为上下文结构的一部分或诸如此类)传递

我觉得你是over-estimating编译器达不到的:

I understood, that __builtin_object_size should give information about the size of an object, if it can be determined at compile time

到这里,好了。

or the relevant call to malloc uses the attribute alloc_size.

但这不是析取(“或”)。编译器只能知道编译时已知的内容。因此,除了能够确定 compile-time 处的大小外,别无选择。现在,如果一个对象是动态分配的,编译器可能仍然能够确定它的大小,前提是:

  • 它是由编译器识别为动态分配函数的函数分配的,并且
  • 编译器可以从动态分配函数的参数中推断出分配对象的大小,并且
  • 编译器可以确定这些参数的值。

对于前两点,编译器依赖于alloc_size属性。如果分配函数没有标明这个属性,编译器就不能确定对象的大小。所以它是一个连词(“和”)。但这还不够;编译器还需要能够确定分配对象时使用的参数值。

要通过函数调用工作,函数调用基本上必须内联。如果只在一个地方调用静态函数,它们通常会被内联。 (它们可能在其他地方内联,但如果从多个调用站点调用或可能调用大型函数,则不太可能将其内联。)

简而言之,如果你说的话会更准确:

__builtin_object_size should give information about the size of an object, if it can be determined at compile time, which requires the relevant call to malloc to have the attribute alloc_size and that its arguments can be determined at compile time.