GCC的安全堆栈保护:未定义引用__stack_chk_guard。

9

我想要启用gcc的canaries生成,但是出现了对__stack_chk_guard的未定义引用。

来自gcc关于canaries的手册:

-mstack-protector-guard=guard
       Generate stack protection code using canary at guard.  Supported locations are global for
       global canary or tls for per-thread canary in the TLS block (the default).  This option
       has effect only when -fstack-protector or -fstack-protector-all is specified.

   These -m switches are supported in addition to the above on x86-64 processors in 64-bit
   environments.

我已经完成了这个测试程序:
#define VALUE 2048
int    main()
{
  char arr[VALUE];
  int  i;

  for (i = 0; i < VALUE + 15; i++) // "i < VALUE + 15" is to test if canaries works but the code doesn't compile anymore with "i < 10" 
    arr[i] = '0';
  return 0;
}

如gcc的手册中所述,我的编译命令行是:

gcc main.c -fstack-protector-all -mstack-protector-guard=global

但我收到以下错误信息:
/tmp/ccXxxxVd.o: In function `main':
main.c:(.text+0xe): undefined reference to `__stack_chk_guard'
main.c:(.text+0x51): undefined reference to `__stack_chk_guard'
collect2: error: ld returned 1 exit status

我该如何消除这个错误?
编辑:
  • 操作系统:Ubuntu 14.10 Utopic
  • 架构:x86-64
  • 环境:64位
4个回答

5
似乎-mstack-protector-guard选项仅用于向后兼容栈保护程序以前的工作方式。在过去,金丝雀是在全局变量中的。后来它被切换到TLS。你使用的操作系统/ libc似乎已经删除或从未支持全局变量canary,所以只有TLS有效。
不要触摸-mstack-protector-guard选项,一切都应该正常工作。当您使用-fstack-protector-all时,默认设置应该很好。

我接受你的答案,即使我觉得奇怪的是Ubuntu没有这个功能。 - Jérémy Pouyet
1
@JérémyPouyet 我怀疑他们认为添加此功能没有任何意义,因为将canaries保留在TLS中而不是全局变量在各个方面都更加优越。 - Art

4
在C文件中为__stack_chk_guard提供一个随机值,避免使用常规值,如全为零或FF的值,因为在任何内存操作期间堆栈都可以轻松获取这些值。维基百科提供了有关提供魔数实现的信息。将此__stack_chk_guard放置在堆栈的顶部和底部,在每次堆栈访问期间都会进行检查。值的任何更改都意味着堆栈已损坏,并通过提供堆栈保护以错误返回结果。
unsigned long __stack_chk_guard;
void __stack_chk_guard_setup(void)
{
     __stack_chk_guard = 0xBAAAAAAD;//provide some magic numbers
}

void __stack_chk_fail(void)                         
{
 /* Error message */                                 
}// will be called when guard variable is corrupted 

不要使用常量;这容易被缓冲区溢出攻击所破解。特别是在32位构建中,long为32位,因此没有一个字节是00, 所以整个32位常量可以简单地成为strcpy缓冲区溢出攻击字符串的一部分。其他类型的缓冲区溢出攻击需要避免其他字节。你希望每次运行时都将其初始化为不同的随机值,以便攻击者即使拥有相同的二进制构建版本,也无法提前知道它是什么。绝对不能是可识别的魔数! - Peter Cordes
这就是为什么有一个设置函数,而不是像 long foo = 0x1234; 这样提供一个静态初始化器。我认为 __stack_chk_guard_setup 在 main 函数运行之前由 init 函数调用,因此您不需要使用静态初始化器在任何函数使用它作为堆栈 cookie 之前将其初始化。(如果您在函数使用它后更改它,则在返回时它会误报检测到堆栈破坏。) - Peter Cordes

2

有两种方法可以解决这个错误:

  1. 从编译器选项中禁用(注释掉)"stack guard"。
  2. 在您的C文件中定义__stack_chk_guard。

当您定义__stack_chk_guard时,请确保为其提供随机值。要提供随机值,您需要将其作为参数传递给random函数。

如需进一步了解详细信息,请参阅编译器手册。


1

对于在自定义链接脚本的裸机软件开发中遇到此错误的人,请确保传递选项-nostdlib

gcc -nostdlib

自Ubuntu 16.04起,编译器默认启用堆栈保护。 man gcc显示:
NOTE: In Ubuntu 14.10 and later versions, -fstack-protector-strong is enabled by default for C, C++, ObjC, ObjC++, if none of -fno-stack-protector, -nostdlib, nor -ffreestanding are found.

-fno-stack-protector 对我也起到了解决作用,但你应该告诉你的编译器你正在进行裸机开发以避免其他类似问题。

我猜测这是因为该功能依赖于通常在没有给出链接器脚本时定义的符号?但是通过使用以下命令未找到有关这些符号的提及:

aarch64-linux-gnu-gcc -Wl,-verbose main.c

所以我不确定。

我在GCC 6.4.0源代码中使用了grep,并且它表明该符号来自libgcc2.c,位于gcc/doc/tm.texi

此挂钩的默认版本创建一个名为@samp{__stack_chk_guard}的变量,默认情况下在@file{libgcc2.c}中定义。


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