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

我们有一些没有人关心的生产制造系统,这些机器运行GCC 3或GCC 2的古代版本。

而且我不能说服pipe理层把它升级到更新的版本,他们说:“如果没有损坏,不要修复”。

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

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

我的问题是:

可以使用旧的C编译器危及已编译程序的安全吗?

更新:

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

其实我会反驳。

有很多情况下,C标准没有定义行为,但在某个特定平台上,“哑编译器”会出现什么情况。 例如允许一个有符号整数溢出或通过两种不同types的variables访问相同的内存。

最近版本的gcc(和clang)已经开始将这些情况视为优化机会而不关心它们是否改变了二进制在“未定义行为”情况下的行为。 如果您的代码库是由像“便携式汇编程序”那样对待C的人编写的,那么这非常糟糕。 随着时间的推移,优化器在进行这些优化时已经开始考虑越来越大的代码块,从而增加了二进制最终将执行除“哑编译器构build的二进制文件”将做的事情之外的可能性。

有编译器开关恢复“传统”行为(-fwrapv和-fno-strict-aliasing为上述两个),但首先你必须知道他们。

虽然原则上编译器错误可能会将兼容的代码转化为安全漏洞,但我认为在macros观计划中这种风险可以忽略不计。

两种行动都有风险。


老的编译器有成熟的优点,而且它们中的任何一个都可能(但不能保证)成功解决了。

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


另一方面,较新的编译器带有附加工具

  • GCC和Clang现在都拥有消毒工具,可以检测运行时间以检测各种不确定行为(Google编译器团队的Chandler Carruth去年声称,他预计他们已经达到全面覆盖)
  • Clang至less具有强化function,例如控制stream完整性 ( Control Flow Integrity)是关于检测控制stream的hi-jacks,还有硬化工具来防止堆栈粉碎攻击(通过将堆栈的控制stream部分与数据部分分开) ; 硬化function通常是低开销(CPU开销<1%)
  • Clang / LLVM也在研究libFuzzer ,这是一个创build仪表化模糊unit testing的工具,可以巧妙地探索被测函数的input空间(通过调整input以采取尚未探索的执行path)

使用消毒剂(Address Sanitizer,Memory Sanitizer或Undefined Behavior Sanitizer)对二进制文件进行testing,然后对其进行模糊处理(例如使用美国的Fuzzy Lop )已经发现了一些高性能软件的漏洞,请参阅本文的LWN.net文章 。

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

通过停留在一个动力不足的编译器上,你正在将自己的头放在沙中,并且穿过手指,没有发现任何漏洞。 如果你的产品是一个高价值的目标,我劝你重新考虑。


注意:即使不升级生产编译器,也可能需要使用新编译器来检查漏洞; 请注意,由于这些是不同的编译器,所以保证减less了。

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

由于testing和修复由于客户错误报告引起的错误,编译代码中的错误率可能很低。 所以最初可能有大量的错误,但是已经下降了。

如果升级到更新的编译器,则可能会丢失编译器错误引入的错误。 但是,这些错误都是你所知没有人发现,也没有人利用的错误。 但是新的编译器本身可能有错误,重要的是新编译器有更强的倾向,把未定义的行为变成编译代码中的错误。

所以在编译的代码中会有很多新的bug。 黑客可以find和利用的所有错误。 除非你做了大量的testing,并且让你的代码和客户一起长时间地发现错误,那么安全性就会降低。

如果没有损坏,请不要修理

你的老板说得对,但是更重要的是保护input,输出,缓冲区溢出。 不pipe使用什么编译器,从这个angular度来看,缺less这些总是无疑是链中最薄弱的环节。

但是,如果代码库是古老的,并且工作到位,以减轻所使用的K&R C的缺点,比如缺乏types安全性,不安全的fgets等,则考虑“ 将编译器升级到更现代的C99 / C11标准打破了一切?

假如有一个清晰的path可以迁移到新的C标准,这可能会产生副作用,可能最好尝试旧代码库的分支,评估它,并进行额外的types检查,完整性检查,并确定是否升级到较新的编译器对input/输出数据集有任何影响。

然后你可以把它展示给你的老板,“ 这是更新的代码库,重构,更符合行业接受的C99 / C11标准…… ”。

那就是必须非常小心的赌博,在这样的环境下可能会出现变化的阻力 ,并可能拒绝接触新的东西。

编辑

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

另外,由于OP提到使用Visual Studio 2008来构build代码库,因此使用gcc可能会导致将MinGW或Cygwin引入到环境中,这可能会对环境产生影响,除非目标是针对Linux,那么它将是值得一试,可能还需要在编译器中join额外的开关,以尽量减less旧的K&R代码库的噪音,另一个重要的是要进行大量的testing,以确保没有function被破坏,可能会变成一个痛苦的练习。

可以使用旧的C编译器危及已编译程序的安全吗?

当然,如果旧的编译器包含已知的错误,你知道会影响你的程序。

问题是,是吗? 要知道,你必须从你的版本读取整个变更日志到现在的date,并检查每年修复的每一个错误。

如果你没有发现编译器bug会影响你的程序的证据,更新GCC只是为了看起来有点偏执。 您必须记住,新版本可能包含尚未发现的新错误。 GCC 5和C11的支持最近有很多变化。

这就是说,80年代编写的代码很可能已经充满了安全漏洞,依赖于定义不明确的行为,无论编译器如何。 我们在这里讨论的是标准的C语言。

恶意开发人员可能通过编译器漏洞潜入后门存在安全风险。 根据正在使用的编译器中已知错误的数量,后门程序可能看起来或多或less不显眼(无论如何,重点在于源代码级别的代码是正确的,即使是错综复杂的)。一个没有bug的编译器不会find后门,因为在这些条件下后门不存在)。 对于额外的拒绝点,恶意开发人员也可能自己寻找以前未知的编译器错误。 同样,伪装的质量将取决于发现的编译器错误的select。

这篇文章中的程序sudo演示了这种攻击。 bcrypt写了一个很棒的Javascript minifiers的后续 。

除此之外,C编译器的发展也越来越 多地采用未定义的行为,所以真正编写的旧C代码实际上会更安全地用C编译器编译,或者编译-O0(但是在新版本的编译器中引入了一些新的破解UB开发优化,即使在-O0 )。

较老的编译器可能无法防止已知的黑客攻击。 例如,堆栈粉碎保护, 直到GCC 4.1才被引入。 所以是的,用较老的编译器编译的代码可能会受到较新编译器的防护。

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

对于某些语言特性,较旧的编译器可能具有不同于程序员所规定和预期的行为。 这种不匹配会减缓开发速度,并引入可被利用的微妙的错误。

旧的编译器提供更less的function(包括语言function!),也不会优化。 程序员会绕过这些缺陷 – 例如,通过重新实现缺失的特性,或者编写不明显但运行速度更快的聪明代码 – 为创build微妙的错误创造新的机会。

原因很简单,旧的编译器可能有旧的bug和漏洞,但是新的编译器会有新的bug和漏洞。

你没有通过升级到新的编译器来“修复”任何错误。 您切换旧的错误和利用新的错误和漏洞。

那么很有可能老版本的编译器中的任何错误都是众所周知的,而不是使用新的编译器,所以可以采取行动,通过编译来避免这些错误。 所以用一种不足以作为升级的参数。 在我工作的地方,我们有相同的讨论,我们在embedded式软件的代码库上使用GCC 4.6.1,由于害怕新的,没有logging的错误,所以升级到最新的编译器是非常不情愿的。

你的问题分为两部分:

  • 显式:“使用旧的编译器有更大的风险”(或多或less的标题)
  • 暗示:“我如何说服pipe理层升级”

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

但是如果你的系统没有受到黑客的攻击,你应该对编译器升级是否会提高你的效率更感兴趣:MSVS 2013代码分析通常比MSVS 2010发现潜在的错误要快得多,它或多或less地支持C99 / C11 – 不知道它是否正式,但声明可以遵循语句,你可以在variables中声明variables。