首页 文章

使用过时的C编译器存在安全风险吗?

提问于
浏览
135

我们有一些 生产环境 中没有人关心的构建系统,这些机器运行古老版本的GCC,如GCC 3或GCC 2 .

我无法说服管理层将其升级到更近期:他们说,“如果没有破产,就不要修理它” .

由于我们维护了一个非常古老的代码库(写于80年代),这个C89代码在这些编译器上编译得很好 .

但我不确定使用这些旧东西是个好主意 .

我的问题是:

可以使用旧的C编译器破坏编译程序的安全性吗?

更新:

相同的代码是由Visual Studio 2008 for Windows目标构建的,而MSVC还不支持C99或C11(我不知道新的MSVC是否支持),我可以使用最新的GCC在我的Linux机器上构建它 . 因此,如果我们只是放入一个较新的GCC,它可能会像以前一样好 .

11 回答

  • 9

    较旧的编译器可能无法抵御已知的黑客攻击 . 例如,堆栈粉碎保护没有被引入until GCC 4.1 . 所以是的,用较旧的编译器编译的代码可能会受到新编译器防范的攻击 .

  • 2

    两种行动方案都存在风险 .


    较旧的编译器具有成熟的优点,并且其中的任何内容都可能(但不能保证)成功解决 .

    在这种情况下,新的编译器是新bug的潜在来源 .


    另一方面,较新的编译器带有额外的工具:

    • GCC和Clang现在都具有消毒剂,可以检测运行时间以检测各种不确定的行为(Google Compiler团队的Chandler Carruth去年声称他希望他们已达到全面覆盖)

    • Clang,至少具有强化功能,例如Control Flow Integrity是关于检测控制流的高压插孔,还有硬化工具来防止堆栈粉碎攻击(通过将堆栈的控制流部分与数据部分分开);强化功能通常是低开销(CPU开销<1%)

    • Clang / LLVM也在开发libFuzzer,这是一个创建仪表化模糊测试单元测试的工具,可以巧妙地探索被测函数的输入空间(通过调整输入来获取尚未探索的执行路径)

    使用消毒剂(Address Sanitizer,Memory Sanitizer或Undefined Behavior Sanitizer)对二进制文件进行检测,然后对其进行模糊测试(例如使用American Fuzzy Lop)已经发现了许多高知名度软件中的漏洞,例如参见LWN.net article .

    除非您升级编译器,否则您无法访问这些新工具和所有未来工具 .

    通过坚持使用功能不足的编译器,您可以将头放在沙子中,并且不会发现任何漏洞 . 如果您的产品是高 Value 目标,我建议您重新考虑 .


    注意:即使您不升级 生产环境 编译器,您也可能希望使用新的编译器来检查漏洞;请注意,由于这些是不同的编译器,但保证会减少 .

  • 0

    您编译的代码包含可被利用的错误 . 这些错误来自三个来源:源代码中的错误,编译器和库中的错误,以及编译器变成错误的源代码中的未定义行为 . (未定义的行为是一个错误,但不是已编译代码中的错误 . 例如,i = i;在C或C中是一个错误,但在您编译的代码中,它可能会增加1并且可以,或者设置为我是一些垃圾,是一个错误) .

    由于测试和修复客户错误报告导致的错误,编译代码中的错误率可能很低 . 因此最初可能存在大量错误,但这已经下降了 .

    如果升级到较新的编译器,则可能会丢失编译器错误引入的错误 . 但是这些错误都会成为你所知道的没有人发现并且没有人被利用的错误 . 但是新编译器本身可能存在错误,而且重要的是,较新的编译器更倾向于将未定义的行为转换为编译代码中的错误 .

    所以你的编译代码中会有很多新bug;黑客可以找到和利用的所有错误 . 除非你做了大量的测试,并且将代码留给客户以便长时间发现错误,否则它将不那么安全 .

  • 6

    存在安全风险,恶意开发人员可能会通过编译器错误进入后门 . 根据正在使用的编译器中已知错误的数量,后门可能看起来或多或少不显眼(无论如何,关键是代码是正确的,即使在源级别进行了复杂处理 . 源代码审查和测试使用一个非错误的编译器将找不到后门,因为在这些条件下不存在后门) . 对于额外的拒绝点,恶意开发人员也可能自己查找以前未知的编译器错误 . 同样,伪装的质量将取决于找到的编译器错误的选择 .

    这个攻击在this article中的程序sudo上有说明 . bcrypt为Javascript minifiers写了一篇很棒的后续文章 .

    除了这种担忧之外,C编译器的发展一直是积极地利用未定义的行为moremore以及more,因此用真诚编写的旧C代码实际上会更安全地使用C编译器从当时编译,或者编译为-O0(但是一些新的破坏程序的UB利用优化are introduced in new versions of compilers even at -O0) .

  • 50

    原因很简单,旧的编译器可能有旧漏洞和漏洞,但新的编译器会有新的漏洞和漏洞利用 .

    您不是通过升级到新编译器来“修复”任何错误 . 您为新的bug和漏洞切换旧漏洞和漏洞利用 .

  • 99

    可以使用旧的C编译器破坏编译程序的安全性吗?

    当然,如果旧编译器包含您知道会影响程序的已知错误,它可以 .

    问题是,是吗?要确切知道,您必须阅读从您的版本到当前日期的整个更改日志,并检查多年来修复的每个错误 .

    如果你没有发现会影响你的程序的编译器错误的证据,那么仅仅为了它而更新GCC似乎有点偏执 . 您必须记住,较新的版本可能包含尚未发现的新错误 . 最近在GCC 5和C11支持下做了很多改变 .

    话虽如此,无论编译器如何,80年代编写的代码很可能已经填满了安全漏洞,并且依赖于定义不明确的行为 . 我们在这里讨论的是预标准C.

  • 5

    您的问题分为两部分:

    • 明确:“使用较旧的编译器是否存在更大的风险”(或多或少与 Headers 中一样)

    • 隐含:“我如何说服管理层升级”

    也许您可以通过在现有代码库中找到可利用的缺陷并显示较新的编译器检测到它来回答这两个问题 . 当然,您的管理层可能会说“您使用旧编译器发现了这一点”,但您可以指出它需要付出相当大的努力 . 或者,如果您能够/允许使用新编译器编译代码,则通过新编译器运行它以查找漏洞,然后利用它 . 您可能需要友好的黑客的帮助,但这取决于信任他们并且能够/允许向他们展示代码(并使用新的编译器) .

    但是,如果你的系统没有暴露给黑客,你或许应该对编译器升级是否会提高你的效率更感兴趣:MSVS 2013 Code Analysis经常比MSVS 2010更早发现潜在的错误,它或多或少支持C99 / C11 - 不确定它是否正式,但声明可以遵循语句,你可以在 for -loops中声明变量 .

  • 19

    好吧,旧编译器中的任何错误都是众所周知和记录的,而不是使用新的编译器,因此可以采取措施通过编码来避免这些错误 . 所以以一种不足以作为升级论据的方式 . 我们在工作中有相同的讨论,我们在嵌入式软件的代码库上使用GCC 4.6.1,并且由于担心新的,未记录的错误,因此很难(在管理层之间)升级到最新的编译器 .

  • 46

    其实我会反驳 .

    在许多情况下,C标准未定义行为,但很明显,a会发生什么给定平台上的“哑编译器” . 允许有符号整数溢出或通过两种不同类型的变量访问相同内存的情况 .

    gcc(和clang)的最新版本已经开始将这些案例视为优化机会,如果它们改变二进制在“未定义行为”条件下的行为方式,则无法照顾 . 如果您的代码库是由处理C的人编写的,就像“便携式汇编程序”一样,这是非常糟糕的 . 随着时间的推移,优化器在进行这些优化时开始考虑越来越大的代码块,这增加了二进制文件最终会做“除了由哑编译器构建的二进制文件”之外的其他事情的机会 .

    有编译器开关来恢复“传统”行为(-fwrapv和-fno-strict-aliasing用于我上面提到的两个),但首先你要了解它们 .

    虽然原则上编译器错误可能会将符合规范的代码转变为安全漏洞,但我会认为这种风险在宏观方案中可以忽略不计 .

  • 7

    如果它没有破坏,请不要修理它

    你的老板说得对,但是,更多 important 因素是保护输入,输出,缓冲区溢出 . 无论使用何种编译器,缺乏这些都是从这个角度来看链中最薄弱的环节 .

    但是,如果代码库是古老的,并且已经采取措施来减轻所使用的K&R C的弱点,例如缺乏类型安全性,不安全的fgets等,那么就要权衡“将编译器升级到更现代的C99 / C11标准打破了一切?“

    如果有一条明确的路径可以迁移到较新的C标准,可能会产生副作用,最好尝试旧代码库的分支,评估它并进行额外的类型检查,健全性检查,并确定是否升级到较新的编译器对输入/输出数据集有任何影响 .

    然后你可以向老板展示,“这是更新的代码库,重构,更符合行业认可的C99 / C11标准......” .

    这是必须权衡的赌博,非常谨慎,对变化的抵制可能会在那种环境中表现出来并且可能拒绝接触新的东西 .

    EDIT

    只是坐了几分钟,意识到这一点,K&R生成的代码可以在16位平台上运行,很有可能,升级到更现代的编译器实际上可能会破坏代码库,我想在架构方面,会生成32位代码,这可能会对用于输入/输出数据集的结构产生有趣的副作用,这是另一个需要仔细权衡的因素 .

    此外,由于OP提到使用Visual Studio 2008来构建代码库,使用gcc可能会导致MinGW或Cygwin进入环境,这可能会对环境产生影响,除非目标是针对Linux,那么它将是值得一试,可能要包括额外的开关到编译器,以尽量减少旧K&R代码库的噪音,另一个重要的事情是进行大量的测试,以确保没有功能被打破,可能结果是一个痛苦的运动 .

  • 9

    另一个需要担心的方面是开发新代码 .

    对于某些语言功能,较旧的编译器可能具有与程序员标准化和期望的不同的行为 . 这种不匹配会减慢开发速度并引入可被利用的细微错误 .

    较旧的编译器提供较少的功能(包括语言功能!),也不进行优化 . 程序员将破解这些缺陷 - 例如通过重新实现缺少的功能,或编写模糊但运行速度更快的聪明代码 - 为创建微妙的错误创造新的机会 .

相关问题