gcc -D_FORTIFY_SOURCE=1 和 -D_FORTIFY_SOURCE=2 之间的区别是什么?

87

有人能指出gcc -D_FORTIFY_SOURCE=1-D_FORTIFY_SOURCE=2之间的区别吗?我猜=2更安全?我还没有找到一个逐点列出差异的列表。

我也读到过-D_FORTIFY_SOURCE=2应该与-O2一起使用,否则不会有所有功能可用。同样,在这里我没有找到一个详细说明回归的列表。我特别想用-Os编译,因为目标是一个闪存不太多的设备。

欢迎任何关于此文档的提示!


10
不确定您是否已经看到这些内容:(1) 包含GCC支持的补丁(其中包含有关=1=2之间差异的一些详细信息)在这里;(2) 关于 _FORTIFY_SOURCE 的“功能测试宏”手册页面在这里 - ArjunShankar
2个回答

76

从功能测试宏的手册页(man 7 feature_test_macros)上可以得知:

_FORTIFY_SOURCE (自glibc 2.3.4起)

定义此宏会导致在使用各种字符串和内存操作函数(例如memcpymemsetstpcpystrcpystrncpystrcatstrncatsprintfsnprintfvsprintfvsnprintfgets以及宽字符变体)时执行一些轻量级的检查,以检测一些缓冲区溢出错误。对于某些函数,还将检查参数的一致性;例如,当指定的标志包括O_CREAT时,将检查是否已为open提供了一个模式参数。并非所有问题都会被检测到,只有一些常见情况。

如果将_FORTIFY_SOURCE设置为1,并且使用编译器优化级别1(gcc -O1)及以上,则执行不应更改符合规范的程序行为的检查。

_FORTIFY_SOURCE设置为2后,会添加一些额外的检查,但某些符合规范的程序可能会失败。

其中一些检查可以在编译时(通过头文件中实现的宏逻辑)进行,并导致编译器警告;其他检查发生在运行时,如果检查失败,则导致运行时错误。

使用此宏需要编译器支持,在gcc 4.0版本及以上可用。

此外,增强应用程序安全性的FORTIFY_SOURCE文章(2014年3月)表示:
  • gcc -D_FORTIFY_SOURCE=1仅在编译时添加检查(需要一些头文件,如#include <string.h>
  • gcc -D_FORTIFY_SOURCE=2还会在运行时添加检查(检测到缓冲区溢出将终止程序)
本质上,_FORTIFY_SOURCE级别2更安全,但是它是一种稍微有风险的编译策略。如果您使用它,请确保对编译代码进行非常强大的回归测试,以证明编译器没有引入任何意外的行为。

9
是否有任何迹象表明符合标准的程序何时可能会失败? - user857990
4
-D_FORTIFY_SOURCE=1 至少添加了一项运行时检查:它启用了 __longjmp_chk 而不是 __longjmp,前者会检查您是否在堆栈中向下跳转。 - Daniel Jour
“二级更安全,但稍微有些风险。” — 看起来有风险的是潜在的溢出问题;如果您的软件没有缓冲区溢出的代码,那么二级是否不符合标准? - Alexis Wilke

27

https://gcc.gnu.org/legacy-ml/gcc-patches/2004-09/msg02055.htmlfeature_test_macros(7)更详细。

以下是相关摘录,经过轻微编辑/重新格式化以提高清晰度:

The difference between -D_FORTIFY_SOURCE=1 and -D_FORTIFY_SOURCE=2 is e.g. for

  struct S {
      struct T {
        char buf[5];
        int x;
      } t;
      char buf[20];
  } var;

With -D_FORTIFY_SOURCE=1,

  strcpy (&var.t.buf[1], "abcdefg");

is not considered an overflow (object is whole var), while with -D_FORTIFY_SOURCE=2

  strcpy (&var.t.buf[1], "abcdefg");

will be considered a buffer overflow.

Another difference is that with -D_FORTIFY_SOURCE=2, %n in format strings of the most common *printf family functions is allowed only if it is stored in read-only memory (usually string literals, gettext's _("%s string %n") is fine too), but usually when an attacker attempts to exploit a format string vulnerability, %n will be somewhere where the attacker could write it into.


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