何时以及如何使用GCC的堆栈保护特性?

71

我正在编译一个商业多平台的C++游戏引擎,使用GCC 4.2在Mac OS X 10.6上进行编译时启用了-Wstack-protector警告。该标志会警告一些未受保护的函数,即使使用了-fstack-protector也无法保护这些函数。

在构建项目时,GCC会发出一些警告:

not protecting function:no buffer at least 8 bytes long
not protecting local variables:variable length buffer

对于第一个警告,我发现可以调整函数中使用的缓冲区的最小大小,以保护该函数免受堆栈破坏:--param ssp-buffer-size=X,其中X默认为8,可以降至1。

对于第二个警告,除非停止使用-Wstack-protector,否则无法避免出现。

  1. 什么情况下应该使用-fstack-protector?(例如,在开发期间始终使用还是只在跟踪错误时使用?)
  2. 什么情况下应该使用-fstack-protector-all
  3. -Wstack-protector告诉我什么?它是在建议我减小缓冲区最小大小吗?
  4. 如果是这样,将大小设置为1是否有任何缺陷?
  5. 似乎-Wstack-protector不是您希望始终启用的标志,如果要获得无警告的构建,是这样吗?

这些选项旨在防止缓冲区溢出/基于堆栈破坏的攻击。以下文章应该会有所帮助:- 什么是堆栈保护程序 - SSP - dirkgently
你需要使用 C99 的可变长度数组 (VLA) 功能吗?如果你不使用它,就不会得到警告 - 也会得到保护。你真的确定使用 VLA 能带来足够的收益,以至于失去保护是可以接受的吗? - Jonathan Leffler
你是否期望人们尝试破解你的代码?如果是,那么这可能是一个好主意。至于性能,你需要计算一下。这里有一个起点:http://www.trl.ibm.com/projects/security/ssp/node5.html#SECTION00051000000000000000 - dirkgently
我宁愿在调试版本中这样做,并在第一版中将其删除,看看人们对游戏的反应(这更重要),并且祈祷有一天我必须加入堆栈保护;-) - dirkgently
你的公司是否有必须遵守的安全威胁模型?如果是这样,那么你需要它。 - dirkgently
显示剩余3条评论
2个回答

80

栈保护是加固策略,不是调试策略。如果你的程序需要从网络或其他不受控制的来源接收数据,请启用它。如果没有从不受控制的地方接收数据,就不要启用。

这是如何运作的:如果你的程序存在 bug,并且基于攻击者可以控制的某些内容更改了缓冲区,则攻击者可以覆盖返回地址或堆栈的类似部分,导致其执行他们的代码而不是你的代码。如果检测到这种情况,栈保护将中止你的程序。你的用户可能不会很满意,但他们也不会被黑客攻击。这并不是指那种通过作弊在游戏中获利的黑客行为,而是指某人利用你代码中的漏洞创建潜在感染用户的漏洞利用(exploit)。

对于面向调试的解决方案,可以考虑使用mudflap等工具。

至于你的具体问题:

  1. 如果你从不受控制的来源获取数据,请使用栈保护。答案可能是肯定的。所以使用它。即使您没有从不受控制的来源接收数据,您也可能最终或已经这么做了而没有意识到。
  2. 如果你想要一些额外的保护以换取一些性能损失,可以为所有缓冲区使用栈保护。从gcc4.4.2手册中得知:
  3. -fstack-protector

    会生成检查缓冲区溢出(例如堆栈破坏攻击)的额外代码。它通过向具有易受攻击对象的函数添加保护变量来实现这一点。这包括调用alloca的函数和具有大于8个字节的缓冲区的函数。当函数进入时初始化这些保护变量,然后在函数退出时检查其状态。如果保护检查失败,将打印错误消息并退出程序。

    -fstack-protector-all

    与-fstack-protector类似,但是所有函数都受到保护。

  • 这些警告告诉您栈保护无法保护哪些缓冲区。

  • 它并不一定建议您减少最小缓冲区大小,在大小为0/1时,它与stack-protector-all相同。 它只是向您指出,以便您如果决定重新设计代码以保护缓冲区。
  • 不,这些警告不代表问题,它们只是向您提供信息。不要经常使用它们。


  • 1
    -fstack-protector 只检查缓冲区大于 "8字节" 的函数的原因是什么? 为什么不是4字节或1字节? - wenchiching
    1
    @wenchiching,原因是性能和溢出的可能性。发现8个字节是溢出可能性值得保护性能成本的阈值。 - brantgurga
    4
    栈保护的调试策略是可行的,有时也很有用:(1) 在调试器中执行启用了栈保护并禁用ASLR(地址空间布局随机化)的二进制文件。(2) 等待核心转储文件。(3) 核心转储文件将指示被破坏的栈canary的地址。(4) 在调试器中重新运行二进制文件,并在被破坏的栈canary的地址上设置硬件监视点。(5) 当位于罪魁祸首地址的内存被修改时,调试器将停止。 - Yeow_Meng
    该机制在以下链接中有详细描述:https://wiki.osdev.org/Stack_Smashing_Protector - Rachid K.

    1

    对于普通构建,您确实不应该关心警告。它实际上更多是一条信息性的消息。我希望显而易见的是,使用栈上变量大小缓冲区确实存在固有的安全问题;如果计算大小错误,您将打开一个大洞。


    使用-Wno-stack-protector禁用所有堆栈破坏警告。 目前还没有禁用仅此警告的#pragma。(这在GCC中经常发生。通常存在没有警告或太少警告之间的紧张关系。一种解决方案是为有问题的文件创建特殊的make目标,并为其设置特殊的编译标志。) - Prof. Falken
    你能详细阐述一下可变大小缓冲区在风险方面与固定大小缓冲区显著不同的原因吗?我能理解为什么它们很难像固定大小缓冲区那样被放置在函数中作为堆栈保护者。 - Jonathan Leffler
    双重攻击向量:攻击者可以提供比您为缓冲区分配的更多输入字节,或者他可以尝试查找您的缓冲区大小分配中的漏洞。 - MSalters

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