acm-header
登录

ACM通信

研究突出了

技术角度:叠加未定义的行为


任何计算机系统都必须在系统保留的自由度和对系统用户的保证之间做出权衡。设计师试图平衡相互冲突的目标,例如吞吐量和易用性。编程语言也必须做出这些权衡。例如,具有内置垃圾收集的语言通常保留了在内存中移动对象的自由,这使得与其他进程或硬件设备共享对象变得困难。

C和c++基于一种极端的权衡:在这些语言中,各种各样难以避免的程序行为,如有符号整数溢出和越界数组引用,都是“未定义的行为”。对于执行未定义行为的程序,根本不作任何保证。这些语言对未定义行为的严重依赖源于C语言过时的“信任程序员”的哲学,也源于标准委员会为包含各种各样的实现所做的务实努力。由于未定义的行为而产生的错误是很难预防的,在过去的几十年里,它们已经导致了安全关键计算机程序中大量可利用的漏洞。

2009年,一位研究人员发现Linux内核中包含的代码在检查指针是否为空之前解除了对指针的引用。C编译器就能够有效地执行以下分析:

情况1:指针不是空的,显示空检查是不必要的。

情况2:指针为空。因为这是未定义的行为,所以编译器没有义务考虑这种情况。

很容易看出,这两种情况都不需要null检查,而编译器未能发出null检查,从而导致内核中存在可利用的漏洞。这个错误被认为是有害的,因为源代码包含必要的空指针检查,但编译的二进制代码不包含。

在接下来的文章中,Wang等人认识到这个Linux错误是一个更广泛的错误类别的成员。他们假设,每当编译器能够通过使用基于未定义行为的推理删除代码时,被编译的程序就可能包含bug。他们的工具STACK可以检测这种“不稳定代码”,它已经被用于在重要的应用程序中发现许多错误。

尽管之前已经在检测未定义行为上投入了大量的精力,但STACK的设计点是有趣的和新的。首先,警告每一个死代码实例的工具是无用的,因为死代码是常见的,而且通常是良性的。STACK检测不稳定代码的不同方法允许它专注于只因为未定义行为而死的特殊类型的代码。根据经验,C和c++开发人员很难手工找到不稳定的代码。其次,STACK不会试图警告那些不会导致不稳定代码的未定义行为。虽然乍一看这可能是一个限制,但在实践中这意味着很大一部分的STACK缺陷报告对开发人员是有用的。最后,用于未定义行为的STACK模型没有绑定到任何特定的编译器。相反,STACK生成关于未定义行为的查询,这些行为被传递给自动化的定理验证器,使它能够检测不稳定的代码,即使没有C或c++编译器能够删除它。

计算社区正在努力清除由于我们庞大的C和c++安装基础中未定义的行为而产生的bug。这些语言被用来实现我们几乎所有最安全关键和安全关键的程序。需要更多新颖的方法,如STACK。

回到顶部

作者

约翰Regehrregehr@cs.utah.edu)是盐湖城犹他大学计算机学院的教授。

回到顶部

脚注

查看所附文件,请访问doi.acm.org/10.1145/2885256


版权归作者所有。

数字图书馆是由计算机协会出版的。版权所有©2016 ACM股份有限公司


没有发现记录

登录为完全访问
»忘记密码? *创建ACM Web帐户
文章内容:
Baidu
map