GCC是否生成Canary?

18

我的gcc版本是4.8.2,操作系统是ubuntu 14.04(64位)。 我发现有时候gcc会自动生成canary以进行缓冲区溢出保护,有时却不会。为什么?

生成canary的情况:当SIZE是4的倍数时。

#include<stdio.h>
#define SIZE 4

int main()
{
    char s[SIZE];
    scanf("%s", s);
    return 0;
}

在使用gcc -c -g -Wa,-a,-ad后的汇编代码

...
   4:a.c           **** int main()
   5:a.c           **** {
  13                    .loc 1 5 0
  14                    .cfi_startproc
  15 0000 55            pushq   %rbp
  16                    .cfi_def_cfa_offset 16
  17                    .cfi_offset 6, -16
  18 0001 4889E5        movq    %rsp, %rbp
  19                    .cfi_def_cfa_register 6
  20 0004 4883EC10      subq    $16, %rsp
  21                    .loc 1 5 0
  22 0008 64488B04      movq    %fs:40, %rax
  22      25280000 
  22      00
  23 0011 488945F8      movq    %rax, -8(%rbp)
  24 0015 31C0          xorl    %eax, %eax
   6:a.c           ****     char s[SIZE];
   7:a.c           ****     scanf("%s", s);
...

不生成canary:不是4的倍数

#include<stdio.h>
#define SIZE 2

int main()
{
    char s[SIZE];
    scanf("%s", s);
    return 0;
}

在使用gcc -c -g -Wa,-a,-ad命令后,进行汇编

...
   4:a.c           **** int main()
   5:a.c           **** {
  13                    .loc 1 5 0
  14                    .cfi_startproc
  15 0000 55            pushq   %rbp
  16                    .cfi_def_cfa_offset 16
  17                    .cfi_offset 6, -16
  18 0001 4889E5        movq    %rsp, %rbp
  19                    .cfi_def_cfa_register 6
  20 0004 4883EC10      subq    $16, %rsp
   6:a.c           ****     char s[SIZE];
   7:a.c           ****     scanf("%s", s);
...

2
你的操作系统是什么(包括名称和版本)?这很重要,因为不同的发行版包含不同的补丁和默认标志(参见)。例如,除非我指定“-fstack-protector”,否则不会为我生成canaries。 - xaizek
我的操作系统是Ubuntu 14.04 64位。 - zongyuwu
1
那么很可能会使用-fstack-protector,但man页面说对于所有具有大于8字节缓冲区的函数都会生成canary,编译器为这两个缓冲区分配了16字节的空间(在你和我机器上一样),因此两个版本都应该有canary。我在相同版本的GCC上得到了不同的行为。你尝试过更大的数字吗,比如17和20? - xaizek
1
@xaizek:文档上说缓冲区大于8,而不是栈帧大于8。也许Ubuntu已经将此限制修补为4了? - rodrigo
1
@rodrigo,可能是。这就是为什么我对更大缓冲区大小的行为感兴趣的原因。最好的方法是查看其他补丁列表,但我不知道在哪里可以找到它(似乎没有列在launchpad.net上)。 - xaizek
显示剩余3条评论
1个回答

21

好的,我想我们已经从评论中知道了答案,所以我将在此明确陈述。

在许多函数中放置金丝雀可能会导致性能下降。这就是为什么有几种方法告诉GCC我们想要使用它们,这些方法在这里很好地描述了。主要思想:

  1. 默认情况下不使用金丝雀,需要传递启用它们的标志之一。
  2. 为了节省执行时间,GCC使用带有-fstack-protector标志的简单启发式方法:为使用alloca或大于8字节(默认值)的本地缓冲区的函数添加金丝雀。
  3. 启发式方法可以使用ssp-buffer-size参数进行微调:--param ssp-buffer-size=4

显然,Ubuntu发行版提供的GCC版本更改了缓冲区的大小为4,因此小于该大小的缓冲区不会触发金丝雀的生成。 我确认(任何其他人都应该能够重复)通过使用--param ssp-buffer-size=4编译两个示例,其中仅为一个生成带有金丝雀的汇编。


动态大小的缓冲区是如何工作的? - Bharath Vemula
1
@BharathVemula 似乎总是为 char s[argc] 生成金丝雀。 - xaizek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接